int ReadTIFF(const char* const filename, WebPPicture* const pic, int keep_alpha, Metadata* const metadata) { TIFF* const tif = TIFFOpen(filename, "r"); uint32 width, height; uint32* raster; int ok = 0; tdir_t dircount; if (tif == NULL) { fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename); return 0; } dircount = TIFFNumberOfDirectories(tif); if (dircount > 1) { fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n" "Only the first will be used, %d will be ignored.\n", dircount - 1); } if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) && TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) { fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n"); return 0; } raster = (uint32*)_TIFFmalloc(width * height * sizeof(*raster)); if (raster != NULL) { if (TIFFReadRGBAImageOriented(tif, width, height, raster, ORIENTATION_TOPLEFT, 1)) { const int stride = width * sizeof(*raster); pic->width = width; pic->height = height; // TIFF data is ABGR #ifdef WORDS_BIGENDIAN TIFFSwabArrayOfLong(raster, width * height); #endif ok = keep_alpha ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride) : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride); } _TIFFfree(raster); } else { fprintf(stderr, "Error allocating TIFF RGBA memory!\n"); } if (ok) { if (metadata != NULL) { ok = ExtractMetadataFromTIFF(tif, metadata); if (!ok) { fprintf(stderr, "Error extracting TIFF metadata!\n"); MetadataFree(metadata); WebPPictureFree(pic); } } } TIFFClose(tif); return ok; }
void _TIFFSwab32BitData(TIFF* tif, tidata_t buf, tsize_t cc) { (void) tif; assert((cc & 3) == 0); TIFFSwabArrayOfLong((uint32*) buf, cc/4); }
void swabData(int type, long size, void* data) { if (!swabflag) return; if ( (type == TIFF_SHORT) || (type == TIFF_SSHORT) ) TIFFSwabArrayOfShort( (uint16*) data, size/2 ); if ( (type == TIFF_LONG) || (type == TIFF_SLONG) || (type == TIFF_FLOAT) ) TIFFSwabArrayOfLong( (uint32*) data, size/4 ); if (type == TIFF_RATIONAL) TIFFSwabArrayOfLong( (uint32*) data, size/4 ); if (type == TIFF_DOUBLE) TIFFSwabArrayOfDouble( (double*) data, size/8 ); }
void TIFFSwabDouble(double *dp) { register uint32* lp = (uint32*) dp; uint32 t; TIFFSwabArrayOfLong(lp, 2); t = lp[0]; lp[0] = lp[1]; lp[1] = t; }
/* * Write a contiguous directory item. */ static int TIFFWriteData(TIFF* tif, TIFFDirEntry* dir, char* cp) { tsize_t cc; if (tif->tif_flags & TIFF_SWAB) { switch (dir->s.tdir_type) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((uint16*) cp, TDIRGetEntryCount(tif,dir)); break; case TIFF_LONG: case TIFF_SLONG: case TIFF_IFD: case TIFF_FLOAT: TIFFSwabArrayOfLong((uint32*) cp, TDIRGetEntryCount(tif,dir)); break; case TIFF_LONG8: case TIFF_SLONG8: case TIFF_IFD8: TIFFSwabArrayOfLong8((uint64*) cp, TDIRGetEntryCount(tif,dir)); break; case TIFF_RATIONAL: case TIFF_SRATIONAL: TIFFSwabArrayOfLong((uint32*) cp, 2 * TDIRGetEntryCount(tif,dir)); break; case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*) cp, TDIRGetEntryCount(tif,dir)); break; } } TDIRSetEntryOff(tif,dir,tif->tif_dataoff); cc = TDIRGetEntryCount(tif,dir) * TIFFDataWidth((TIFFDataType) dir->s.tdir_type); if (SeekOK(tif, tif->tif_dataoff) && WriteOK(tif, cp, cc)) { tif->tif_dataoff += ((cc + 1) & ~1); return (1); } TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing data for field \"%s\" (%d)", _TIFFFieldWithTag(tif, dir->s.tdir_tag)->field_name, TIFFGetErrno(tif)); return (0); }
/* * Fetch a contiguous directory item. */ static tsize_t TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp) { int w = tiffDataWidth[dir->tdir_type]; tsize_t cc = dir->tdir_count * w; if (!isMapped(tif)) { if (!SeekOK(tif, dir->tdir_offset)) goto bad; if (!ReadOK(tif, cp, cc)) goto bad; } else { if (dir->tdir_offset + cc > tif->tif_size) goto bad; _TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc); } if (tif->tif_flags & TIFF_SWAB) { switch (dir->tdir_type) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((uint16*) cp, dir->tdir_count); break; case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabArrayOfLong((uint32*) cp, dir->tdir_count); break; case TIFF_RATIONAL: case TIFF_SRATIONAL: TIFFSwabArrayOfLong((uint32*) cp, 2*dir->tdir_count); break; case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*) cp, dir->tdir_count); break; } } return (cc); bad: TIFFError(tif->tif_name, "Error fetching data for field \"%s\"", _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); return ((tsize_t) 0); }
void TIFFSwabArrayOfDouble(double* dp, register unsigned long n) { register uint32* lp = (uint32*) dp; register uint32 t; TIFFSwabArrayOfLong(lp, n + n); while (n-- > 0) { t = lp[0]; lp[0] = lp[1]; lp[1] = t; lp += 2; } }
static int initImage(void) { uint32 w, h; if (order) TIFFSetField(tif, TIFFTAG_FILLORDER, order); if (photo != (uint16) -1) TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photo); if (!TIFFRGBAImageBegin(&img, tif, stoponerr, title)) { TIFFError(filelist[fileindex], "%s", title); TIFFClose(tif); tif = NULL; return -1; } /* * Setup the image raster as required. */ h = img.height; w = img.width; if (h > ymax) { w = (int)(w * ((float)ymax / h)); h = ymax; } if (w > xmax) { h = (int)(h * ((float)xmax / w)); w = xmax; } if (w != width || h != height) { uint32 rastersize = _TIFFMultiply32(tif, img.width, img.height, "allocating raster buffer"); if (raster != NULL) _TIFFfree(raster), raster = NULL; raster = (uint32*) _TIFFCheckMalloc(tif, rastersize, sizeof (uint32), "allocating raster buffer"); if (raster == NULL) { width = height = 0; TIFFError(filelist[fileindex], "No space for raster buffer"); cleanup_and_exit(); } width = w; height = h; } TIFFRGBAImageGet(&img, raster, img.width, img.height); #if HOST_BIGENDIAN TIFFSwabArrayOfLong(raster,img.width*img.height); #endif return 0; }
int ReadTIFF(const char* const filename, WebPPicture* const pic, int keep_alpha) { TIFF* const tif = TIFFOpen(filename, "r"); uint32 width, height; uint32* raster; int ok = 0; int dircount = 1; if (tif == NULL) { fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename); return 0; } while (TIFFReadDirectory(tif)) ++dircount; if (dircount > 1) { fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n" "Only the first will be used, %d will be ignored.\n", dircount - 1); } TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); raster = (uint32*)_TIFFmalloc(width * height * sizeof(*raster)); if (raster != NULL) { if (TIFFReadRGBAImageOriented(tif, width, height, raster, ORIENTATION_TOPLEFT, 1)) { const int stride = width * sizeof(*raster); pic->width = width; pic->height = height; // TIFF data is ABGR #ifdef __BIG_ENDIAN__ TIFFSwabArrayOfLong(raster, width * height); #endif ok = keep_alpha ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride) : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride); } _TIFFfree(raster); } else { fprintf(stderr, "Error allocating TIFF RGBA memory!\n"); } if (ok && keep_alpha == 2) { WebPCleanupTransparentArea(pic); } TIFFClose(tif); return ok; }
/* * Write a contiguous directory item. */ static int TIFFWriteData(TIFF* tif, TIFFDirEntry* dir, char* cp) { tsize_t cc; if (tif->tif_flags & TIFF_SWAB) { switch (dir->tdir_type) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((uint16*) cp, dir->tdir_count); break; case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabArrayOfLong((uint32*) cp, dir->tdir_count); break; case TIFF_RATIONAL: case TIFF_SRATIONAL: TIFFSwabArrayOfLong((uint32*) cp, 2*dir->tdir_count); break; case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*) cp, dir->tdir_count); break; } } dir->tdir_offset = tif->tif_dataoff; cc = dir->tdir_count * TIFFDataWidth((TIFFDataType) dir->tdir_type); if (SeekOK(tif, dir->tdir_offset) && WriteOK(tif, cp, cc)) { tif->tif_dataoff += (cc + 1) & ~1; return (1); } TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing data for field \"%s\"", _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); return (0); }
static void swapBytesInScanline(void *buf, uint32 width, TIFFDataType dtype) { switch (dtype) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((uint16*)buf, (unsigned long)width); break; case TIFF_LONG: case TIFF_SLONG: TIFFSwabArrayOfLong((uint32*)buf, (unsigned long)width); break; /* case TIFF_FLOAT: */ /* FIXME */ case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*)buf, (unsigned long)width); break; default: break; } }
G_MODULE_EXPORT gboolean tracker_extract_get_metadata (TrackerExtractInfo *info) { TIFF *image; TrackerXmpData *xd = NULL; TrackerIptcData *id = NULL; TrackerExifData *ed = NULL; MergeData md = { 0 }; TiffData td = { 0 }; gchar *filename, *uri; gchar *date; glong exif_offset; GPtrArray *keywords; guint i; GFile *file; TrackerSparqlBuilder *metadata, *preupdate; const gchar *graph, *urn; int fd; #ifdef HAVE_LIBIPTCDATA gchar *iptc_offset; guint32 iptc_size; #endif #ifdef HAVE_EXEMPI gchar *xmp_offset; guint32 size; #endif /* HAVE_EXEMPI */ file = tracker_extract_info_get_file (info); filename = g_file_get_path (file); preupdate = tracker_extract_info_get_preupdate_builder (info); metadata = tracker_extract_info_get_metadata_builder (info); graph = tracker_extract_info_get_graph (info); urn = tracker_extract_info_get_urn (info); fd = tracker_file_open_fd (filename); if (fd == -1) { g_warning ("Could not open tiff file '%s': %s\n", filename, g_strerror (errno)); g_free (filename); return FALSE; } if ((image = TIFFFdOpen (fd, filename, "r")) == NULL){ g_warning ("Could not open image:'%s'\n", filename); g_free (filename); close (fd); return FALSE; } tracker_sparql_builder_predicate (metadata, "a"); tracker_sparql_builder_object (metadata, "nfo:Image"); tracker_sparql_builder_object (metadata, "nmm:Photo"); uri = g_file_get_uri (file); #ifdef HAVE_LIBIPTCDATA if (TIFFGetField (image, TIFFTAG_RICHTIFFIPTC, &iptc_size, &iptc_offset)) { if (TIFFIsByteSwapped(image) != 0) { TIFFSwabArrayOfLong((uint32*) iptc_offset, (unsigned long) iptc_size); } id = tracker_iptc_new (iptc_offset, 4 * iptc_size, uri); } #endif /* HAVE_LIBIPTCDATA */ if (!id) { id = g_new0 (TrackerIptcData, 1); } /* FIXME There are problems between XMP data embedded with different tools due to bugs in the original spec (type) */ #ifdef HAVE_EXEMPI if (TIFFGetField (image, TIFFTAG_XMLPACKET, &size, &xmp_offset)) { xd = tracker_xmp_new (xmp_offset, size, uri); } #endif /* HAVE_EXEMPI */ if (!xd) { xd = g_new0 (TrackerXmpData, 1); } ed = g_new0 (TrackerExifData, 1); /* Get Tiff specifics */ td.width = tag_to_string (image, TIFFTAG_IMAGEWIDTH, TAG_TYPE_UINT32); td.length = tag_to_string (image, TIFFTAG_IMAGELENGTH, TAG_TYPE_UINT32); td.artist = tag_to_string (image, TIFFTAG_ARTIST, TAG_TYPE_STRING); td.copyright = tag_to_string (image, TIFFTAG_COPYRIGHT, TAG_TYPE_STRING); date = tag_to_string (image, TIFFTAG_DATETIME, TAG_TYPE_STRING); td.date = tracker_date_guess (date); g_free (date); td.title = tag_to_string (image, TIFFTAG_DOCUMENTNAME, TAG_TYPE_STRING); td.description = tag_to_string (image, TIFFTAG_IMAGEDESCRIPTION, TAG_TYPE_STRING); td.make = tag_to_string (image, TIFFTAG_MAKE, TAG_TYPE_STRING); td.model = tag_to_string (image, TIFFTAG_MODEL, TAG_TYPE_STRING); td.orientation = get_orientation (image); /* Get Exif specifics */ if (TIFFGetField (image, TIFFTAG_EXIFIFD, &exif_offset)) { if (TIFFReadEXIFDirectory (image, exif_offset)) { ed->exposure_time = tag_to_string (image, EXIFTAG_EXPOSURETIME, TAG_TYPE_DOUBLE); ed->fnumber = tag_to_string (image, EXIFTAG_FNUMBER, TAG_TYPE_DOUBLE); ed->iso_speed_ratings = tag_to_string (image, EXIFTAG_ISOSPEEDRATINGS, TAG_TYPE_C16_UINT16); date = tag_to_string (image, EXIFTAG_DATETIMEORIGINAL, TAG_TYPE_STRING); ed->time_original = tracker_date_guess (date); g_free (date); ed->metering_mode = get_metering_mode (image); ed->flash = get_flash (image); ed->focal_length = tag_to_string (image, EXIFTAG_DATETIMEORIGINAL, TAG_TYPE_DOUBLE); ed->white_balance = get_white_balance (image); /* ed->software = tag_to_string (image, EXIFTAG_SOFTWARE, TAG_TYPE_STRING); */ } } TIFFClose (image); g_free (filename); md.title = tracker_coalesce_strip (5, xd->title, xd->pdf_title, td.title, ed->document_name, xd->title2); md.orientation = tracker_coalesce_strip (4, xd->orientation, td.orientation, ed->orientation, id->image_orientation); md.copyright = tracker_coalesce_strip (4, xd->rights, td.copyright, ed->copyright, id->copyright_notice); md.white_balance = tracker_coalesce_strip (2, xd->white_balance, ed->white_balance); md.fnumber = tracker_coalesce_strip (2, xd->fnumber, ed->fnumber); md.flash = tracker_coalesce_strip (2, xd->flash, ed->flash); md.focal_length = tracker_coalesce_strip (2, xd->focal_length, ed->focal_length); md.artist = tracker_coalesce_strip (4, xd->artist, td.artist, ed->artist, xd->contributor); md.exposure_time = tracker_coalesce_strip (2, xd->exposure_time, ed->exposure_time); md.iso_speed_ratings = tracker_coalesce_strip (2, xd->iso_speed_ratings, ed->iso_speed_ratings); md.date = tracker_coalesce_strip (6, xd->date, xd->time_original, td.date, ed->time, id->date_created, ed->time_original); md.description = tracker_coalesce_strip (3, xd->description, td.description, ed->description); md.metering_mode = tracker_coalesce_strip (2, xd->metering_mode, ed->metering_mode); md.city = tracker_coalesce_strip (2, xd->city, id->city); md.state = tracker_coalesce_strip (2, xd->state, id->state); md.address = tracker_coalesce_strip (2, xd->address, id->sublocation); md.country = tracker_coalesce_strip (2, xd->country, id->country_name); /* FIXME We are not handling the altitude ref here for xmp */ md.gps_altitude = tracker_coalesce_strip (2, xd->gps_altitude, ed->gps_altitude); md.gps_latitude = tracker_coalesce_strip (2, xd->gps_latitude, ed->gps_latitude); md.gps_longitude = tracker_coalesce_strip (2, xd->gps_longitude, ed->gps_longitude); md.gps_direction = tracker_coalesce_strip (2, xd->gps_direction, ed->gps_direction); md.creator = tracker_coalesce_strip (3, xd->creator, id->byline, id->credit); md.x_dimension = tracker_coalesce_strip (2, td.width, ed->x_dimension); md.y_dimension = tracker_coalesce_strip (2, td.length, ed->y_dimension); md.make = tracker_coalesce_strip (3, xd->make, td.make, ed->make); md.model = tracker_coalesce_strip (3, xd->model, td.model, ed->model); keywords = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); if (ed->user_comment) { tracker_sparql_builder_predicate (metadata, "nie:comment"); tracker_sparql_builder_object_unvalidated (metadata, ed->user_comment); } if (md.x_dimension) { tracker_sparql_builder_predicate (metadata, "nfo:width"); tracker_sparql_builder_object_unvalidated (metadata, md.x_dimension); } if (md.y_dimension) { tracker_sparql_builder_predicate (metadata, "nfo:height"); tracker_sparql_builder_object_unvalidated (metadata, md.y_dimension); } if (xd->keywords) { tracker_keywords_parse (keywords, xd->keywords); } if (xd->pdf_keywords) { tracker_keywords_parse (keywords, xd->pdf_keywords); } if (xd->subject) { tracker_keywords_parse (keywords, xd->subject); } if (xd->publisher) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", xd->publisher); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, xd->publisher); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:publisher"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (xd->type) { tracker_sparql_builder_predicate (metadata, "dc:type"); tracker_sparql_builder_object_unvalidated (metadata, xd->type); } if (xd->format) { tracker_sparql_builder_predicate (metadata, "dc:format"); tracker_sparql_builder_object_unvalidated (metadata, xd->format); } if (xd->identifier) { tracker_sparql_builder_predicate (metadata, "dc:identifier"); tracker_sparql_builder_object_unvalidated (metadata, xd->identifier); } if (xd->source) { tracker_sparql_builder_predicate (metadata, "dc:source"); tracker_sparql_builder_object_unvalidated (metadata, xd->source); } if (xd->language) { tracker_sparql_builder_predicate (metadata, "dc:language"); tracker_sparql_builder_object_unvalidated (metadata, xd->language); } if (xd->relation) { tracker_sparql_builder_predicate (metadata, "dc:relation"); tracker_sparql_builder_object_unvalidated (metadata, xd->relation); } if (xd->coverage) { tracker_sparql_builder_predicate (metadata, "dc:coverage"); tracker_sparql_builder_object_unvalidated (metadata, xd->coverage); } if (xd->rating) { tracker_sparql_builder_predicate (metadata, "nao:numericRating"); tracker_sparql_builder_object_unvalidated (metadata, xd->rating); } if (xd->license) { tracker_sparql_builder_predicate (metadata, "nie:license"); tracker_sparql_builder_object_unvalidated (metadata, xd->license); } if (xd->regions) { tracker_xmp_apply_regions (preupdate, metadata, graph, xd); } if (md.address || md.state || md.country || md.city || md.gps_altitude || md.gps_latitude || md.gps_longitude) { tracker_sparql_builder_predicate (metadata, "slo:location"); tracker_sparql_builder_object_blank_open (metadata); /* GeoPoint */ tracker_sparql_builder_predicate (metadata, "a"); tracker_sparql_builder_object (metadata, "slo:GeoLocation"); if (md.address || md.state || md.country || md.city) { gchar *addruri; addruri = tracker_sparql_get_uuid_urn (); tracker_sparql_builder_predicate (metadata, "slo:postalAddress"); tracker_sparql_builder_object_iri (metadata, addruri); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, addruri); g_free (addruri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:PostalAddress"); if (md.address) { tracker_sparql_builder_predicate (preupdate, "nco:streetAddress"); tracker_sparql_builder_object_unvalidated (preupdate, md.address); } if (md.state) { tracker_sparql_builder_predicate (preupdate, "nco:region"); tracker_sparql_builder_object_unvalidated (preupdate, md.state); } if (md.city) { tracker_sparql_builder_predicate (preupdate, "nco:locality"); tracker_sparql_builder_object_unvalidated (preupdate, md.city); } if (md.country) { tracker_sparql_builder_predicate (preupdate, "nco:country"); tracker_sparql_builder_object_unvalidated (preupdate, md.country); } if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); } if (md.gps_altitude) { tracker_sparql_builder_predicate (metadata, "slo:altitude"); tracker_sparql_builder_object_unvalidated (metadata, md.gps_altitude); } if (md.gps_latitude) { tracker_sparql_builder_predicate (metadata, "slo:latitude"); tracker_sparql_builder_object_unvalidated (metadata, md.gps_latitude); } if (md.gps_longitude) { tracker_sparql_builder_predicate (metadata, "slo:longitude"); tracker_sparql_builder_object_unvalidated (metadata, md.gps_longitude); } tracker_sparql_builder_object_blank_close (metadata); /* GeoLocation */ } if (md.gps_direction) { tracker_sparql_builder_predicate (metadata, "nfo:heading"); tracker_sparql_builder_object_unvalidated (metadata, md.gps_direction); } if (id->contact) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", id->contact); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, id->contact); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:representative"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (id->keywords) { tracker_keywords_parse (keywords, id->keywords); } for (i = 0; i < keywords->len; i++) { gchar *escaped, *subject; const gchar *p; p = g_ptr_array_index (keywords, i); escaped = tracker_sparql_escape_string (p); subject = g_strdup_printf ("_:tag%d", i + 1); /* ensure tag with specified label exists */ tracker_sparql_builder_insert_open (preupdate, graph); tracker_sparql_builder_subject (preupdate, subject); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nao:Tag"); tracker_sparql_builder_predicate (preupdate, "nao:prefLabel"); tracker_sparql_builder_object_unvalidated (preupdate, escaped); tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_append (preupdate, "WHERE { FILTER (NOT EXISTS { " "?tag a nao:Tag ; nao:prefLabel \""); tracker_sparql_builder_append (preupdate, escaped); tracker_sparql_builder_append (preupdate, "\" }) }\n"); /* associate file with tag */ tracker_sparql_builder_insert_open (preupdate, graph); tracker_sparql_builder_subject_iri (preupdate, urn); tracker_sparql_builder_predicate (preupdate, "nao:hasTag"); tracker_sparql_builder_object (preupdate, "?tag"); tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_where_open (preupdate); tracker_sparql_builder_subject (preupdate, "?tag"); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nao:Tag"); tracker_sparql_builder_predicate (preupdate, "nao:prefLabel"); tracker_sparql_builder_object_unvalidated (preupdate, escaped); tracker_sparql_builder_where_close (preupdate); g_free (subject); g_free (escaped); } g_ptr_array_free (keywords, TRUE); if (md.make || md.model) { gchar *equip_uri; equip_uri = tracker_sparql_escape_uri_printf ("urn:equipment:%s:%s:", md.make ? md.make : "", md.model ? md.model : ""); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, equip_uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nfo:Equipment"); if (md.make) { tracker_sparql_builder_predicate (preupdate, "nfo:manufacturer"); tracker_sparql_builder_object_unvalidated (preupdate, md.make); } if (md.model) { tracker_sparql_builder_predicate (preupdate, "nfo:model"); tracker_sparql_builder_object_unvalidated (preupdate, md.model); } if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nfo:equipment"); tracker_sparql_builder_object_iri (metadata, equip_uri); g_free (equip_uri); } tracker_guarantee_title_from_file (metadata, "nie:title", md.title, uri, NULL); if (md.orientation) { tracker_sparql_builder_predicate (metadata, "nfo:orientation"); tracker_sparql_builder_object_unvalidated (metadata, md.orientation); } if (md.copyright) { tracker_sparql_builder_predicate (metadata, "nie:copyright"); tracker_sparql_builder_object_unvalidated (metadata, md.copyright); } if (md.white_balance) { tracker_sparql_builder_predicate (metadata, "nmm:whiteBalance"); tracker_sparql_builder_object_unvalidated (metadata, md.white_balance); } if (md.fnumber) { tracker_sparql_builder_predicate (metadata, "nmm:fnumber"); tracker_sparql_builder_object_unvalidated (metadata, md.fnumber); } if (md.flash) { tracker_sparql_builder_predicate (metadata, "nmm:flash"); tracker_sparql_builder_object_unvalidated (metadata, md.flash); } if (md.focal_length) { tracker_sparql_builder_predicate (metadata, "nmm:focalLength"); tracker_sparql_builder_object_unvalidated (metadata, md.focal_length); } if (md.artist) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", md.artist); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, md.artist); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:contributor"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (md.exposure_time) { tracker_sparql_builder_predicate (metadata, "nmm:exposureTime"); tracker_sparql_builder_object_unvalidated (metadata, md.exposure_time); } if (md.iso_speed_ratings) { tracker_sparql_builder_predicate (metadata, "nmm:isoSpeed"); tracker_sparql_builder_object_unvalidated (metadata, md.iso_speed_ratings); } tracker_guarantee_date_from_file_mtime (metadata, "nie:contentCreated", md.date, uri); if (md.description) { tracker_sparql_builder_predicate (metadata, "nie:description"); tracker_sparql_builder_object_unvalidated (metadata, md.description); } if (md.metering_mode) { tracker_sparql_builder_predicate (metadata, "nmm:meteringMode"); tracker_sparql_builder_object_unvalidated (metadata, md.metering_mode); } if (md.creator) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", md.creator); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, md.creator); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); /* NOTE: We only have affiliation with * nco:PersonContact and we are using * nco:Contact here. */ /* if (id->byline_title) { */ /* tracker_sparql_builder_insert_open (preupdate, NULL); */ /* tracker_sparql_builder_subject (preupdate, "_:affiliation_by_line"); */ /* tracker_sparql_builder_predicate (preupdate, "a"); */ /* tracker_sparql_builder_object (preupdate, "nco:Affiliation"); */ /* tracker_sparql_builder_predicate (preupdate, "nco:title"); */ /* tracker_sparql_builder_object_unvalidated (preupdate, id->byline_title); */ /* tracker_sparql_builder_insert_close (preupdate); */ /* tracker_sparql_builder_predicate (metadata, "a"); */ /* tracker_sparql_builder_object (metadata, "nco:PersonContact"); */ /* tracker_sparql_builder_predicate (metadata, "nco:hasAffiliation"); */ /* tracker_sparql_builder_object (metadata, "_:affiliation_by_line"); */ /* } */ tracker_sparql_builder_predicate (metadata, "nco:creator"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (ed->x_resolution) { gdouble value; value = ed->resolution_unit != 3 ? g_strtod (ed->x_resolution, NULL) : g_strtod (ed->x_resolution, NULL) * CM_TO_INCH; tracker_sparql_builder_predicate (metadata, "nfo:horizontalResolution"); tracker_sparql_builder_object_double (metadata, value); } if (ed->y_resolution) { gdouble value; value = ed->resolution_unit != 3 ? g_strtod (ed->y_resolution, NULL) : g_strtod (ed->y_resolution, NULL) * CM_TO_INCH; tracker_sparql_builder_predicate (metadata, "nfo:verticalResolution"); tracker_sparql_builder_object_double (metadata, value); } tiff_data_free (&td); tracker_exif_free (ed); tracker_xmp_free (xd); tracker_iptc_free (id); g_free (uri); close (fd); return TRUE; }
/* * Function to convert standard TIFF file to BigTIFF file. Loop through all directories in * current file (including subdirectories linked via SubIFD arrays) and create corresponding * new directories with 64-bit arrays. The old 32-bit directories are left in the file as * dead space. The data for directory entries are reused by default. Some directory entries * require new data with 64-bit offsets (e.g. stripe/tile offsets and SubIFD arrays). * * Returns 1 if successful, 0 if cannot convert to BigTIFF (TIFF_NOBIGTIFF set * or 32-bit API called). */ static int TIFFMakeBigTIFF(TIFF *tif) { uint32 dirlink = TIFF_HEADER_DIROFF_S, diroff = tif->tif_header.s.tiff_diroff; toff_t dirlinkB = TIFF_HEADER_DIROFF_B, diroffB; uint16 dircount, dirindex; uint64 dircountB; tsize_t dirsize, dirsizeB; int issubifd = 0; uint32 subifdcnt = 0; uint32 subifdlink; toff_t subifdlinkB; char *data, *dataB; TIFFDirEntry *dir, *dirB; /* * Update flags and file header */ if (noBigTIFF(tif)) { TIFFErrorExt((tif)->tif_clientdata, "TIFFCheckBigTIFF", "File > 2^32 and NO BigTIFF specified"); return (0); } tif->tif_flags |= TIFF_ISBIGTIFF; tif->tif_header.b.tiff_version = TIFF_BIGTIFF_VERSION; tif->tif_header.b.tiff_offsize = 8; tif->tif_header.b.tiff_fill = 0; tif->tif_header.b.tiff_diroff = 0; if (tif->tif_flags & TIFF_SWAB) { TIFFSwabShort(&tif->tif_header.b.tiff_version); TIFFSwabShort(&tif->tif_header.b.tiff_offsize); } if (!SeekOK(tif, 0) || !WriteOK(tif, &tif->tif_header, sizeof(TIFFHeader))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error updating TIFF header (%d)", TIFFGetErrno(tif)); return (0); } if (tif->tif_flags & TIFF_SWAB) { TIFFSwabShort(&tif->tif_header.b.tiff_version); TIFFSwabShort(&tif->tif_header.b.tiff_offsize); } /* * Loop through all directories and rewrite as BigTIFF with 64-bit offsets. This * begins with main IFD chain but may divert to SubIFD arrays as needed. */ while (diroff != 0 && diroff != tif->tif_diroff) { if (!SeekOK(tif, diroff) || !ReadOK(tif, &dircount, sizeof(dircount))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error reading TIFF directory (%d)", TIFFGetErrno(tif)); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); dircountB = dircount; if (!(data = _TIFFmalloc(dirsize = dircount * TIFFDirEntryLenS)) || !(dataB = _TIFFmalloc(dirsizeB = dircount * TIFFDirEntryLenB))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error allocating space for directory"); return (0); } if (!SeekOK(tif, diroff + sizeof(dircount)) || !ReadOK(tif, data, dirsize)) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error reading TIFF directory (%d)", TIFFGetErrno(tif)); goto error; } diroffB = tif->tif_dataoff; tif->tif_dataoff += sizeof(dircountB) + dirsizeB + sizeof(toff_t); for (dirindex = 0; dirindex < dircount; dirindex++) { dir = (TIFFDirEntry*) (data + dirindex * TIFFDirEntryLenS); dirB = (TIFFDirEntry*) (dataB + dirindex * TIFFDirEntryLenB); if (tif->tif_flags & TIFF_SWAB) { TIFFSwabShort(&dir->s.tdir_tag); TIFFSwabShort(&dir->s.tdir_type); TIFFSwabLong(&dir->s.tdir_count); TIFFSwabLong(&dir->s.tdir_offset); } dirB->b.tdir_tag = dir->s.tdir_tag; dirB->b.tdir_type = dir->s.tdir_type; dirB->b.tdir_count = dir->s.tdir_count; dirB->b.tdir_offset = 0; /* * If data are in directory entry itself, copy data, else (data are pointed * to by directory entry) copy pointer. This is complicated by the fact that * the old entry had 32-bits of space, and the new has 64-bits, so may have * to read data pointed at by the old entry directly into the new entry. */ switch (dir->s.tdir_type) { case TIFF_UNDEFINED: case TIFF_BYTE: case TIFF_SBYTE: case TIFF_ASCII: if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset)) _TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count); else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count)) { TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET); TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count); } else dirB->b.tdir_offset = dir->s.tdir_offset; break; case TIFF_SHORT: case TIFF_SSHORT: if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset) / sizeof(uint16)) _TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count * sizeof(uint16)); else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count) / sizeof(uint16)) { TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET); TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * sizeof(uint16)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfShort((uint16*) &dirB->b.tdir_offset, dir->s.tdir_count); } else dirB->b.tdir_offset = dir->s.tdir_offset; break; case TIFF_LONG: case TIFF_FLOAT: case TIFF_IFD: if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset) / sizeof(uint32)) _TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count * sizeof(uint32)); else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count) / sizeof(uint32)) { TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET); TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * sizeof(uint32)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong((uint32*) &dirB->b.tdir_offset, dir->s.tdir_count); } else dirB->b.tdir_offset = dir->s.tdir_offset; break; case TIFF_RATIONAL: case TIFF_SRATIONAL: if (dir->s.tdir_count * 2 <= sizeof(dirB->b.tdir_offset) / sizeof(uint32)) { TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET); TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * 2 * sizeof(uint32)); if (tif->tif_flags & TIFF_SWAB) TIFFSwabArrayOfLong((uint32*) &dirB->b.tdir_offset, dir->s.tdir_count * 2); } else dirB->b.tdir_offset = dir->s.tdir_offset; break; default: dirB->b.tdir_offset = dir->s.tdir_offset; break; } /* * Process special case of SUBIFD; must change data from 32-bit to 64-bit in * case new 64-bit directory offsets need to be added. */ switch (dirB->b.tdir_tag) { case TIFFTAG_SUBIFD: dirB->b.tdir_type = TIFF_IFD8; subifdcnt = dir->s.tdir_count; /* * Set pointer to existing SubIFD array */ if (subifdcnt <= sizeof(dir->s.tdir_offset) / sizeof(uint32)) subifdlink = diroff + sizeof(dircount) + (char*) &dir->s.tdir_offset - (char*) dir; else subifdlink = dir->s.tdir_offset; /* * Initialize new SubIFD array, set pointer to it */ if (subifdcnt <= sizeof(dir->b.tdir_offset) / sizeof(toff_t)) { dir->b.tdir_offset = 0; subifdlinkB = diroffB + sizeof(dircountB) + (char*) &dir->b.tdir_offset - (char*) dirB; } else { toff_t *offB; if (!(offB = _TIFFmalloc(subifdcnt * sizeof(toff_t)))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error allocating space for 64-bit SubIFDs"); goto error; } _TIFFmemset(offB, 0, subifdcnt * sizeof(toff_t)); TIFFWriteLong8Array(tif, dirB, offB); _TIFFfree(offB); subifdlinkB = dirB->b.tdir_offset; } break; } if (tif->tif_flags & TIFF_SWAB) { TIFFSwabShort(&dirB->b.tdir_tag); TIFFSwabShort(&dirB->b.tdir_type); TIFFSwabLong8(&dirB->b.tdir_count); TIFFSwabLong8(&dirB->b.tdir_offset); } } /* * Write out new directory and chain to previous */ if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&dircountB); if (!SeekOK(tif, diroffB) || !WriteOK(tif, &dircountB, sizeof(dircountB)) || !SeekOK(tif, diroffB + sizeof(dircountB)) || !WriteOK(tif, dataB, dirsizeB)) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing TIFF directory (%d)", TIFFGetErrno(tif)); goto error; } /* * If directory is SubIFD update array in host directory, else add to * main directory chain */ if (tif->tif_nsubifd && tif->tif_subifdoff == subifdlink) tif->tif_subifdoff = subifdlinkB; if (!issubifd && dirlinkB == TIFF_HEADER_DIROFF_B) tif->tif_header.b.tiff_diroff = diroffB; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&diroffB); if (!SeekOK(tif, (issubifd ? subifdlinkB++ : dirlinkB)) || !WriteOK(tif, &diroffB, sizeof(diroffB))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory link (%d)", TIFFGetErrno(tif)); goto error; } if (issubifd) subifdcnt--; else { dirlink = diroff + sizeof(dircount) + dirsize; dirlinkB = diroffB + sizeof(dircountB) + dirsizeB; } issubifd = (subifdcnt > 0); if (!SeekOK(tif, (issubifd ? subifdlink++ : dirlink)) || !ReadOK(tif, &diroff, sizeof(diroff))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error reading directory link (%d)", TIFFGetErrno(tif)); goto error; } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&diroff); _TIFFfree(dataB); _TIFFfree(data); } /* * Mark end of directory chain */ diroffB = 0; if (dirlinkB == TIFF_HEADER_DIROFF_B) tif->tif_header.b.tiff_diroff = diroffB; if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong8(&diroffB); if (!SeekOK(tif, dirlinkB) || !WriteOK(tif, &diroffB, sizeof(diroffB))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory link (%d)", TIFFGetErrno(tif)); goto error; } return (1); error: _TIFFfree(dataB); _TIFFfree(data); return (0); }
CPLErr EXIFExtractMetadata(char**& papszMetadata, void *fpInL, int nOffset, int bSwabflag, int nTIFFHEADER, int& nExifOffset, int& nInterOffset, int& nGPSOffset) { GUInt16 nEntryCount; int space; unsigned int n,i; char pszTemp[MAXSTRINGLENGTH]; char pszName[128]; VSILFILE* fp = (VSILFILE* )fpInL; TIFFDirEntry *poTIFFDirEntry; TIFFDirEntry *poTIFFDir; const struct tagname *poExifTags ; const struct intr_tag *poInterTags = intr_tags; const struct gpsname *poGPSTags; /* -------------------------------------------------------------------- */ /* Read number of entry in directory */ /* -------------------------------------------------------------------- */ if( VSIFSeekL(fp, nOffset+nTIFFHEADER, SEEK_SET) != 0 || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fp) != sizeof(GUInt16) ) { CPLError( CE_Failure, CPLE_AppDefined, "Error reading EXIF Directory count at %d.", nOffset + nTIFFHEADER ); return CE_Failure; } if (bSwabflag) TIFFSwabShort(&nEntryCount); // Some apps write empty directories - see bug 1523. if( nEntryCount == 0 ) return CE_None; // Some files are corrupt, a large entry count is a sign of this. if( nEntryCount > 125 ) { CPLError( CE_Warning, CPLE_AppDefined, "Ignoring EXIF directory with unlikely entry count (%d).", nEntryCount ); return CE_Warning; } poTIFFDir = (TIFFDirEntry *)CPLMalloc(nEntryCount * sizeof(TIFFDirEntry)); /* -------------------------------------------------------------------- */ /* Read all directory entries */ /* -------------------------------------------------------------------- */ n = VSIFReadL(poTIFFDir, 1,nEntryCount*sizeof(TIFFDirEntry),fp); if (n != nEntryCount*sizeof(TIFFDirEntry)) { CPLError( CE_Failure, CPLE_AppDefined, "Could not read all directories"); CPLFree(poTIFFDir); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Parse all entry information in this directory */ /* -------------------------------------------------------------------- */ for(poTIFFDirEntry = poTIFFDir,i=nEntryCount; i > 0; i--,poTIFFDirEntry++) { if (bSwabflag) { TIFFSwabShort(&poTIFFDirEntry->tdir_tag); TIFFSwabShort(&poTIFFDirEntry->tdir_type); TIFFSwabLong (&poTIFFDirEntry->tdir_count); TIFFSwabLong (&poTIFFDirEntry->tdir_offset); } /* -------------------------------------------------------------------- */ /* Find Tag name in table */ /* -------------------------------------------------------------------- */ pszName[0] = '\0'; pszTemp[0] = '\0'; for (poExifTags = tagnames; poExifTags->tag; poExifTags++) if(poExifTags->tag == poTIFFDirEntry->tdir_tag) { CPLAssert( NULL != poExifTags && NULL != poExifTags->name ); strcpy(pszName, poExifTags->name); break; } if( nOffset == nGPSOffset) { for( poGPSTags = gpstags; poGPSTags->tag != 0xffff; poGPSTags++ ) if( poGPSTags->tag == poTIFFDirEntry->tdir_tag ) { CPLAssert( NULL != poGPSTags && NULL != poGPSTags->name ); strcpy(pszName, poGPSTags->name); break; } } /* -------------------------------------------------------------------- */ /* If the tag was not found, look into the interoperability table */ /* -------------------------------------------------------------------- */ if( nOffset == nInterOffset ) { for(poInterTags = intr_tags; poInterTags->tag; poInterTags++) if(poInterTags->tag == poTIFFDirEntry->tdir_tag) { CPLAssert( NULL != poInterTags && NULL != poInterTags->name ); strcpy(pszName, poInterTags->name); break; } } /* -------------------------------------------------------------------- */ /* Save important directory tag offset */ /* -------------------------------------------------------------------- */ if( poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG ) nExifOffset=poTIFFDirEntry->tdir_offset; if( poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET ) nInterOffset=poTIFFDirEntry->tdir_offset; if( poTIFFDirEntry->tdir_tag == GPSOFFSETTAG ) { nGPSOffset=poTIFFDirEntry->tdir_offset; } /* -------------------------------------------------------------------- */ /* If we didn't recognise the tag just ignore it. To see all */ /* tags comment out the continue. */ /* -------------------------------------------------------------------- */ if( pszName[0] == '\0' ) { sprintf( pszName, "EXIF_%d", poTIFFDirEntry->tdir_tag ); continue; } /* -------------------------------------------------------------------- */ /* For UserComment we need to ignore the language binding and */ /* just return the actual contents. */ /* -------------------------------------------------------------------- */ if( EQUAL(pszName,"EXIF_UserComment") ) { poTIFFDirEntry->tdir_type = TIFF_ASCII; if( poTIFFDirEntry->tdir_count >= 8 ) { poTIFFDirEntry->tdir_count -= 8; poTIFFDirEntry->tdir_offset += 8; } } /* -------------------------------------------------------------------- */ /* Make some UNDEFINED or BYTE fields ASCII for readability. */ /* -------------------------------------------------------------------- */ if( EQUAL(pszName,"EXIF_ExifVersion") || EQUAL(pszName,"EXIF_FlashPixVersion") || EQUAL(pszName,"EXIF_MakerNote") || EQUAL(pszName,"GPSProcessingMethod") ) poTIFFDirEntry->tdir_type = TIFF_ASCII; /* -------------------------------------------------------------------- */ /* Print tags */ /* -------------------------------------------------------------------- */ int nDataWidth = TIFFDataWidth((TIFFDataType) poTIFFDirEntry->tdir_type); space = poTIFFDirEntry->tdir_count * nDataWidth; /* Previous multiplication could overflow, hence this additional check */ if (poTIFFDirEntry->tdir_count > MAXSTRINGLENGTH) { CPLError( CE_Warning, CPLE_AppDefined, "Too many bytes in tag: %u, ignoring tag.", poTIFFDirEntry->tdir_count ); } else if (nDataWidth == 0 || poTIFFDirEntry->tdir_type >= TIFF_IFD ) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid or unhandled EXIF data type: %d, ignoring tag.", poTIFFDirEntry->tdir_type ); } /* -------------------------------------------------------------------- */ /* This is at most 4 byte data so we can read it from tdir_offset */ /* -------------------------------------------------------------------- */ else if (space >= 0 && space <= 4) { unsigned char data[4]; memcpy(data, &poTIFFDirEntry->tdir_offset, 4); if (bSwabflag) { // Unswab 32bit value, and reswab per data type. TIFFSwabLong((GUInt32*) data); switch (poTIFFDirEntry->tdir_type) { case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabLong((GUInt32*) data); break; case TIFF_SSHORT: case TIFF_SHORT: TIFFSwabArrayOfShort((GUInt16*) data, poTIFFDirEntry->tdir_count); break; default: break; } } EXIFPrintData(pszTemp, poTIFFDirEntry->tdir_type, poTIFFDirEntry->tdir_count, data); } /* -------------------------------------------------------------------- */ /* The data is being read where tdir_offset point to in the file */ /* -------------------------------------------------------------------- */ else if (space > 0 && space < MAXSTRINGLENGTH) { unsigned char *data = (unsigned char *)VSIMalloc(space); if (data) { VSIFSeekL(fp,poTIFFDirEntry->tdir_offset+nTIFFHEADER,SEEK_SET); VSIFReadL(data, 1, space, fp); if (bSwabflag) { switch (poTIFFDirEntry->tdir_type) { case TIFF_SHORT: case TIFF_SSHORT: TIFFSwabArrayOfShort((GUInt16*) data, poTIFFDirEntry->tdir_count); break; case TIFF_LONG: case TIFF_SLONG: case TIFF_FLOAT: TIFFSwabArrayOfLong((GUInt32*) data, poTIFFDirEntry->tdir_count); break; case TIFF_RATIONAL: case TIFF_SRATIONAL: TIFFSwabArrayOfLong((GUInt32*) data, 2*poTIFFDirEntry->tdir_count); break; case TIFF_DOUBLE: TIFFSwabArrayOfDouble((double*) data, poTIFFDirEntry->tdir_count); break; default: break; } } EXIFPrintData(pszTemp, poTIFFDirEntry->tdir_type, poTIFFDirEntry->tdir_count, data); CPLFree(data); } } else { CPLError( CE_Warning, CPLE_AppDefined, "Invalid EXIF header size: %ld, ignoring tag.", (long) space ); } papszMetadata = CSLSetNameValue(papszMetadata, pszName, pszTemp); } CPLFree(poTIFFDir); return CE_None; }
/* This function will write an entire directory to the disk, and return the offset value indicating where in the file it wrote the beginning of the directory structure. This is NOT the same as the offset value before calling this function, because some of the fields may have caused various data items to be written out BEFORE writing the directory structure. This code was basically written by ripping of the TIFFWriteDirectory() code and generalizing it, using RPS's TIFFWritePliIfd() code for inspiration. My original goal was to make this code general enough that the original TIFFWriteDirectory() could be rewritten to just call this function with the appropriate field and field-accessing arguments. However, now I realize that there's a lot of code that gets executed for the main, standard TIFF directories that does not apply to special private subdirectories, so such a reimplementation for the sake of eliminating redundant or duplicate code is probably not possible, unless we also pass in a Main flag to indiciate which type of handling to do, which would be kind of a hack. I've marked those places where I changed or ripped out code which would have to be re-inserted to generalize this function. If it can be done in a clean and graceful way, it would be a great way to generalize the TIFF library. Otherwise, I'll just leave this code here where it duplicates but remains on top of and hopefully mostly independent of the main TIFF library. The caller will probably want to free the sub directory structure after returning from this call, since otherwise once written out, the user is likely to forget about it and leave data lying around. */ toff_t TIFFWritePrivateDataSubDirectory(TIFF* tif, uint32 pdir_fieldsset[], int pdir_fields_last, TIFFFieldInfo *field_info, int (*getFieldFn)(TIFF *tif, ttag_t tag, ...)) { uint16 dircount; uint32 diroff, nextdiroff; ttag_t tag; uint32 nfields; tsize_t dirsize; char* data; TIFFDirEntry* dir; u_long b, *fields, fields_size; toff_t directory_offset; TIFFFieldInfo* fip; /* * Deleted out all of the encoder flushing and such code from here - * not necessary for subdirectories. */ /* Finish writing out any image data. */ TIFFFlushData(tif); /* * Size the directory so that we can calculate * offsets for the data items that aren't kept * in-place in each field. */ nfields = 0; for (b = 0; b <= pdir_fields_last; b++) if (FieldSet(pdir_fieldsset, b)) /* Deleted code to make size of first 4 tags 2 instead of 1. */ nfields += 1; dirsize = nfields * sizeof (TIFFDirEntry); data = (char*) _TIFFmalloc(dirsize); if (data == NULL) { TIFFError(tif->tif_name, "Cannot write private subdirectory, out of space"); return (0); } /* * Place directory in data section of the file. If there isn't one * yet, place it at the end of the file. The directory is treated as * data, so we don't link it into the directory structure at all. */ if (tif->tif_dataoff == 0) tif->tif_dataoff =(TIFFSeekFile(tif, (toff_t) 0, SEEK_END)+1) &~ 1; diroff = tif->tif_dataoff; tif->tif_dataoff = (toff_t)( diroff + sizeof (uint16) + dirsize + sizeof (toff_t)); if (tif->tif_dataoff & 1) tif->tif_dataoff++; (void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET); /*tif->tif_curdir++;*/ dir = (TIFFDirEntry*) data; /* * Setup external form of directory * entries and write data items. */ /* * We make a local copy of the fieldsset here so that we don't mess * up the original one when we call ResetFieldBit(). But I'm not sure * why the original code calls ResetFieldBit(), since we're already * going through the fields in order... * * fields_size is the number of uint32's we will need to hold the * bit-mask for all of the fields. If our highest field number is * 100, then we'll need 100 / (8*4)+1 == 4 uint32's to hold the * fieldset. * * Unlike the original code, we allocate fields dynamically based * on the requested pdir_fields_last value, allowing private * data subdirectories to contain more than the built-in code's limit * of 95 tags in a directory. */ fields_size = pdir_fields_last / (8*sizeof(uint32)) + 1; fields = _TIFFmalloc(fields_size*sizeof(uint32)); _TIFFmemcpy(fields, pdir_fieldsset, fields_size * sizeof(uint32)); /* Deleted "write out extra samples tag" code here. */ /* Deleted code for checking a billion little special cases for the * standard TIFF tags. Should add a general mechanism for overloading * write function for each field, just like Brian kept telling me!!! */ for (fip = field_info; fip->field_tag; fip++) { /* Deleted code to check for FIELD_IGNORE!! */ if (/* fip->field_bit == FIELD_IGNORE || */ !FieldSet(fields, fip->field_bit)) continue; if (!TIFFWriteNormalSubTag(tif, dir, fip, getFieldFn)) goto bad; dir++; ResetFieldBit(fields, fip->field_bit); } /* Now we've written all of the referenced data, and are about to write the main directory structure, so grab the tif_dataoff value now so we can remember where we wrote the directory. */ directory_offset = tif->tif_dataoff; /* * Write directory. */ dircount = (uint16) nfields; /* Deleted code to link to the next directory - we set it to zero! */ nextdiroff = 0; if (tif->tif_flags & TIFF_SWAB) { /* * The file's byte order is opposite to the * native machine architecture. We overwrite * the directory information with impunity * because it'll be released below after we * write it to the file. Note that all the * other tag construction routines assume that * we do this byte-swapping; i.e. they only * byte-swap indirect data. */ for (dir = (TIFFDirEntry*) data; dircount; dir++, dircount--) { TIFFSwabArrayOfShort(&dir->tdir_tag, 2); TIFFSwabArrayOfLong(&dir->tdir_count, 2); } dircount = (uint16) nfields; TIFFSwabShort(&dircount); TIFFSwabLong(&nextdiroff); } (void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET); if (!WriteOK(tif, &dircount, sizeof (dircount))) { TIFFError(tif->tif_name, "Error writing private subdirectory count"); goto bad; } if (!WriteOK(tif, data, dirsize)) { TIFFError(tif->tif_name, "Error writing private subdirectory contents"); goto bad; } if (!WriteOK(tif, &nextdiroff, sizeof (nextdiroff))) { TIFFError(tif->tif_name, "Error writing private subdirectory link"); goto bad; } tif->tif_dataoff += sizeof(dircount) + dirsize + sizeof(nextdiroff); _TIFFfree(data); _TIFFfree(fields); tif->tif_flags &= ~TIFF_DIRTYDIRECT; #if (0) /* This stuff commented out because I don't think we want it for subdirectories, but I could be wrong. */ (*tif->tif_cleanup)(tif); /* * Reset directory-related state for subsequent * directories. */ TIFFDefaultDirectory(tif); tif->tif_curoff = 0; tif->tif_row = (uint32) -1; tif->tif_curstrip = (tstrip_t) -1; #endif return (directory_offset); bad: _TIFFfree(data); _TIFFfree(fields); return (0); }
void TIFFInput::readspec (bool read_meta) { uint32 width = 0, height = 0, depth = 0; unsigned short nchans = 1; TIFFGetField (m_tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (m_tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_IMAGEDEPTH, &depth); TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLESPERPIXEL, &nchans); if (read_meta) { // clear the whole m_spec and start fresh m_spec = ImageSpec ((int)width, (int)height, (int)nchans); } else { // assume m_spec is valid, except for things that might differ // between MIP levels m_spec.width = (int)width; m_spec.height = (int)height; m_spec.depth = (int)depth; m_spec.full_x = 0; m_spec.full_y = 0; m_spec.full_z = 0; m_spec.full_width = (int)width; m_spec.full_height = (int)height; m_spec.full_depth = (int)depth; m_spec.nchannels = (int)nchans; } float x = 0, y = 0; TIFFGetField (m_tif, TIFFTAG_XPOSITION, &x); TIFFGetField (m_tif, TIFFTAG_YPOSITION, &y); m_spec.x = (int)x; m_spec.y = (int)y; m_spec.z = 0; // FIXME? - TIFF spec describes the positions as in resolutionunit. // What happens if this is not unitless pixels? Are we interpreting // it all wrong? if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &width) == 1 && width > 0) m_spec.full_width = width; if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &height) == 1 && height > 0) m_spec.full_height = height; if (TIFFIsTiled (m_tif)) { TIFFGetField (m_tif, TIFFTAG_TILEWIDTH, &m_spec.tile_width); TIFFGetField (m_tif, TIFFTAG_TILELENGTH, &m_spec.tile_height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_TILEDEPTH, &m_spec.tile_depth); } else { m_spec.tile_width = 0; m_spec.tile_height = 0; m_spec.tile_depth = 0; } m_bitspersample = 8; TIFFGetField (m_tif, TIFFTAG_BITSPERSAMPLE, &m_bitspersample); m_spec.attribute ("oiio:BitsPerSample", (int)m_bitspersample); unsigned short sampleformat = SAMPLEFORMAT_UINT; TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); switch (m_bitspersample) { case 1: case 2: case 4: case 6: // Make 1, 2, 4, 6 bpp look like byte images case 8: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT8); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT8); else m_spec.set_format (TypeDesc::UINT8); // punt break; case 10: case 12: case 14: // Make 10, 12, 14 bpp look like 16 bit images case 16: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT16); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT16); else if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::HALF); // not to spec, but why not? else m_spec.set_format (TypeDesc::UNKNOWN); break; case 32: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::FLOAT); else if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT32); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT32); else m_spec.set_format (TypeDesc::UNKNOWN); break; case 64: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::DOUBLE); else m_spec.set_format (TypeDesc::UNKNOWN); break; default: m_spec.set_format (TypeDesc::UNKNOWN); break; } // If we've been instructed to skip reading metadata, because it is // guaranteed to be identical to what we already have in m_spec, // skip everything following. if (! read_meta) return; // Use the table for all the obvious things that can be mindlessly // shoved into the image spec. for (int i = 0; tiff_tag_table[i].name; ++i) find_tag (tiff_tag_table[i].tifftag, tiff_tag_table[i].tifftype, tiff_tag_table[i].name); // Now we need to get fields "by hand" for anything else that is less // straightforward... m_photometric = (m_spec.nchannels == 1 ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB); TIFFGetField (m_tif, TIFFTAG_PHOTOMETRIC, &m_photometric); m_spec.attribute ("tiff:PhotometricInterpretation", (int)m_photometric); if (m_photometric == PHOTOMETRIC_PALETTE) { // Read the color map unsigned short *r = NULL, *g = NULL, *b = NULL; TIFFGetField (m_tif, TIFFTAG_COLORMAP, &r, &g, &b); ASSERT (r != NULL && g != NULL && b != NULL); m_colormap.clear (); m_colormap.insert (m_colormap.end(), r, r + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), g, g + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), b, b + (1 << m_bitspersample)); // Palette TIFF images are always 3 channels (to the client) m_spec.nchannels = 3; m_spec.default_channel_names (); // FIXME - what about palette + extra (alpha?) channels? Is that // allowed? And if so, ever encountered in the wild? } TIFFGetFieldDefaulted (m_tif, TIFFTAG_PLANARCONFIG, &m_planarconfig); m_separate = (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1 && m_photometric != PHOTOMETRIC_PALETTE); m_spec.attribute ("tiff:PlanarConfiguration", (int)m_planarconfig); if (m_planarconfig == PLANARCONFIG_SEPARATE) m_spec.attribute ("planarconfig", "separate"); else m_spec.attribute ("planarconfig", "contig"); int compress = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_COMPRESSION, &compress); m_spec.attribute ("tiff:Compression", compress); switch (compress) { case COMPRESSION_NONE : m_spec.attribute ("compression", "none"); break; case COMPRESSION_LZW : m_spec.attribute ("compression", "lzw"); break; case COMPRESSION_CCITTRLE : m_spec.attribute ("compression", "ccittrle"); break; case COMPRESSION_DEFLATE : case COMPRESSION_ADOBE_DEFLATE : m_spec.attribute ("compression", "zip"); break; case COMPRESSION_PACKBITS : m_spec.attribute ("compression", "packbits"); break; default: break; } int rowsperstrip = -1; if (! m_spec.tile_width) { TIFFGetField (m_tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); if (rowsperstrip > 0) m_spec.attribute ("tiff:RowsPerStrip", rowsperstrip); } // The libtiff docs say that only uncompressed images, or those with // rowsperstrip==1, support random access to scanlines. m_no_random_access = (compress != COMPRESSION_NONE && rowsperstrip != 1); short resunit = -1; TIFFGetField (m_tif, TIFFTAG_RESOLUTIONUNIT, &resunit); switch (resunit) { case RESUNIT_NONE : m_spec.attribute ("ResolutionUnit", "none"); break; case RESUNIT_INCH : m_spec.attribute ("ResolutionUnit", "in"); break; case RESUNIT_CENTIMETER : m_spec.attribute ("ResolutionUnit", "cm"); break; } get_matrix_attribute ("worldtocamera", TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA); get_matrix_attribute ("worldtoscreen", TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN); get_int_attribute ("tiff:subfiletype", TIFFTAG_SUBFILETYPE); // FIXME -- should subfiletype be "conventionized" and used for all // plugins uniformly? // Do we care about fillorder? No, the TIFF spec says, "We // recommend that FillOrder=2 (lsb-to-msb) be used only in // special-purpose applications". So OIIO will assume msb-to-lsb // convention until somebody finds a TIFF file in the wild that // breaks this assumption. // Special names for shadow maps char *s = NULL; TIFFGetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, &s); if (s) m_emulate_mipmap = true; if (s && ! strcmp (s, "Shadow")) { for (int c = 0; c < m_spec.nchannels; ++c) m_spec.channelnames[c] = "z"; } unsigned short *sampleinfo = NULL; unsigned short extrasamples = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); // std::cerr << "Extra samples = " << extrasamples << "\n"; bool alpha_is_unassociated = false; // basic assumption if (extrasamples) { // If the TIFF ExtraSamples tag was specified, use that to figure // out the meaning of alpha. int colorchannels = 3; if (m_photometric == PHOTOMETRIC_MINISWHITE || m_photometric == PHOTOMETRIC_MINISBLACK || m_photometric == PHOTOMETRIC_PALETTE || m_photometric == PHOTOMETRIC_MASK) colorchannels = 1; for (int i = 0, c = colorchannels; i < extrasamples && c < m_spec.nchannels; ++i, ++c) { // std::cerr << " extra " << i << " " << sampleinfo[i] << "\n"; if (sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) { // This is the alpha channel, associated as usual m_spec.alpha_channel = c; } else if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) { // This is the alpha channel, but color is unassociated m_spec.alpha_channel = c; alpha_is_unassociated = true; m_spec.attribute ("oiio:UnassociatedAlpha", 1); } else { DASSERT (sampleinfo[i] == EXTRASAMPLE_UNSPECIFIED); // This extra channel is not alpha at all. Undo any // assumptions we previously made about this channel. if (m_spec.alpha_channel == c) { m_spec.channelnames[c] = Strutil::format("channel%d", c); m_spec.alpha_channel = -1; } } } if (m_spec.alpha_channel >= 0) m_spec.channelnames[m_spec.alpha_channel] = "A"; } // Will we need to do alpha conversions? m_convert_alpha = (m_spec.alpha_channel >= 0 && alpha_is_unassociated && ! m_keep_unassociated_alpha); // N.B. we currently ignore the following TIFF fields: // GrayResponseCurve GrayResponseUnit // MaxSampleValue MinSampleValue // NewSubfileType SubfileType(deprecated) // Colorimetry fields // Search for an EXIF IFD in the TIFF file, and if found, rummage // around for Exif fields. #if TIFFLIB_VERSION > 20050912 /* compat with old TIFF libs - skip Exif */ int exifoffset = 0; if (TIFFGetField (m_tif, TIFFTAG_EXIFIFD, &exifoffset) && TIFFReadEXIFDirectory (m_tif, exifoffset)) { for (int i = 0; exif_tag_table[i].name; ++i) find_tag (exif_tag_table[i].tifftag, exif_tag_table[i].tifftype, exif_tag_table[i].name); // I'm not sure what state TIFFReadEXIFDirectory leaves us. // So to be safe, close and re-seek. TIFFClose (m_tif); #ifdef _WIN32 std::wstring wfilename = Filesystem::path_to_windows_native (m_filename); m_tif = TIFFOpenW (wfilename.c_str(), "rm"); #else m_tif = TIFFOpen (m_filename.c_str(), "rm"); #endif TIFFSetDirectory (m_tif, m_subimage); // A few tidbits to look for ImageIOParameter *p; if ((p = m_spec.find_attribute ("Exif:ColorSpace", TypeDesc::INT))) { // Exif spec says that anything other than 0xffff==uncalibrated // should be interpreted to be sRGB. if (*(const int *)p->data() != 0xffff) m_spec.attribute ("oiio::ColorSpace", "sRGB"); } } #endif #if TIFFLIB_VERSION >= 20051230 // Search for IPTC metadata in IIM form -- but older versions of // libtiff botch the size, so ignore it for very old libtiff. int iptcsize = 0; const void *iptcdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_RICHTIFFIPTC, &iptcsize, &iptcdata)) { std::vector<uint32> iptc ((uint32 *)iptcdata, (uint32 *)iptcdata+iptcsize); if (TIFFIsByteSwapped (m_tif)) TIFFSwabArrayOfLong ((uint32*)&iptc[0], iptcsize); decode_iptc_iim (&iptc[0], iptcsize*4, m_spec); } #endif // Search for an XML packet containing XMP (IPTC, Exif, etc.) int xmlsize = 0; const void *xmldata = NULL; if (TIFFGetField (m_tif, TIFFTAG_XMLPACKET, &xmlsize, &xmldata)) { // std::cerr << "Found XML data, size " << xmlsize << "\n"; if (xmldata && xmlsize) { std::string xml ((const char *)xmldata, xmlsize); decode_xmp (xml, m_spec); } } #if 0 // Experimental -- look for photoshop data int photoshopsize = 0; const void *photoshopdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_PHOTOSHOP, &photoshopsize, &photoshopdata)) { std::cerr << "Found PHOTOSHOP data, size " << photoshopsize << "\n"; if (photoshopdata && photoshopsize) { // std::string photoshop ((const char *)photoshopdata, photoshopsize); // std::cerr << "PHOTOSHOP:\n" << photoshop << "\n---\n"; } } #endif }
static int cvt_by_strip( TIFF *in, TIFF *out ) { uint32* raster; /* retrieve RGBA image */ uint32 width, height; /* image width & height */ uint32 row; uint32 *wrk_line; int ok = 1; TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height); if( !TIFFGetField(in, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) ) { TIFFError(TIFFFileName(in), "Source image not in strips"); return (0); } TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip); /* * Allocate strip buffer */ raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32)); if (raster == 0) { TIFFError(TIFFFileName(in), "No space for raster buffer"); return (0); } /* * Allocate a scanline buffer for swapping during the vertical * mirroring pass. */ wrk_line = (uint32*)_TIFFmalloc(width * sizeof (uint32)); if (!wrk_line) { TIFFError(TIFFFileName(in), "No space for raster scanline buffer"); ok = 0; } /* * Loop over the strips. */ for( row = 0; ok && row < height; row += rowsperstrip ) { int rows_to_write, i_row; /* Read the strip into an RGBA array */ if (!TIFFReadRGBAStrip(in, row, raster)) { ok = 0; break; } /* * XXX: raster array has 4-byte unsigned integer type, that is why * we should rearrange it here. */ #if HOST_BIGENDIAN TIFFSwabArrayOfLong(raster, width * rowsperstrip); #endif /* * Figure out the number of scanlines actually in this strip. */ if( row + rowsperstrip > height ) rows_to_write = height - row; else rows_to_write = rowsperstrip; /* * For some reason the TIFFReadRGBAStrip() function chooses the * lower left corner as the origin. Vertically mirror scanlines. */ for( i_row = 0; i_row < rows_to_write / 2; i_row++ ) { uint32 *top_line, *bottom_line; top_line = raster + width * i_row; bottom_line = raster + width * (rows_to_write-i_row-1); _TIFFmemcpy(wrk_line, top_line, 4*width); _TIFFmemcpy(top_line, bottom_line, 4*width); _TIFFmemcpy(bottom_line, wrk_line, 4*width); } /* * Write out the result in a strip */ if( TIFFWriteEncodedStrip( out, row / rowsperstrip, raster, 4 * rows_to_write * width ) == -1 ) { ok = 0; break; } } _TIFFfree( raster ); _TIFFfree( wrk_line ); return ok; }
static int cvt_whole_image( TIFF *in, TIFF *out ) { uint32* raster; /* retrieve RGBA image */ uint32 width, height; /* image width & height */ uint32 row; size_t pixel_count; TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height); pixel_count = width * height; /* XXX: Check the integer overflow. */ if (!width || !height || pixel_count / width != height) { TIFFError(TIFFFileName(in), "Malformed input file; can't allocate buffer for raster of %lux%lu size", (unsigned long)width, (unsigned long)height); return 0; } rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip); TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip); raster = (uint32*)_TIFFCheckMalloc(in, pixel_count, sizeof(uint32), "raster buffer"); if (raster == 0) { TIFFError(TIFFFileName(in), "Failed to allocate buffer (%lu elements of %lu each)", (unsigned long)pixel_count, (unsigned long)sizeof(uint32)); return (0); } /* Read the image in one chunk into an RGBA array */ if (!TIFFReadRGBAImageOriented(in, width, height, raster, ORIENTATION_TOPLEFT, 0)) { _TIFFfree(raster); return (0); } /* * XXX: raster array has 4-byte unsigned integer type, that is why * we should rearrange it here. */ #if HOST_BIGENDIAN TIFFSwabArrayOfLong(raster, width * height); #endif /* * Do we want to strip away alpha components? */ if (no_alpha) { size_t count = pixel_count; unsigned char *src, *dst; src = dst = (unsigned char *) raster; while (count > 0) { *(dst++) = *(src++); *(dst++) = *(src++); *(dst++) = *(src++); src++; count--; } } /* * Write out the result in strips */ for (row = 0; row < height; row += rowsperstrip) { unsigned char * raster_strip; int rows_to_write; int bytes_per_pixel; if (no_alpha) { raster_strip = ((unsigned char *) raster) + 3 * row * width; bytes_per_pixel = 3; } else { raster_strip = (unsigned char *) (raster + row * width); bytes_per_pixel = 4; } if( row + rowsperstrip > height ) rows_to_write = height - row; else rows_to_write = rowsperstrip; if( TIFFWriteEncodedStrip( out, row / rowsperstrip, raster_strip, bytes_per_pixel * rows_to_write * width ) == -1 ) { _TIFFfree( raster ); return 0; } } _TIFFfree( raster ); return 1; }
static int cvt_by_tile( TIFF *in, TIFF *out ) { uint32* raster; /* retrieve RGBA image */ uint32 width, height; /* image width & height */ uint32 tile_width, tile_height; uint32 row, col; uint32 *wrk_line; int ok = 1; TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height); if( !TIFFGetField(in, TIFFTAG_TILEWIDTH, &tile_width) || !TIFFGetField(in, TIFFTAG_TILELENGTH, &tile_height) ) { TIFFError(TIFFFileName(in), "Source image not tiled"); return (0); } TIFFSetField(out, TIFFTAG_TILEWIDTH, tile_width ); TIFFSetField(out, TIFFTAG_TILELENGTH, tile_height ); /* * Allocate tile buffer */ raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32)); if (raster == 0) { TIFFError(TIFFFileName(in), "No space for raster buffer"); return (0); } /* * Allocate a scanline buffer for swapping during the vertical * mirroring pass. */ wrk_line = (uint32*)_TIFFmalloc(tile_width * sizeof (uint32)); if (!wrk_line) { TIFFError(TIFFFileName(in), "No space for raster scanline buffer"); ok = 0; } /* * Loop over the tiles. */ for( row = 0; ok && row < height; row += tile_height ) { for( col = 0; ok && col < width; col += tile_width ) { uint32 i_row; /* Read the tile into an RGBA array */ if (!TIFFReadRGBATile(in, col, row, raster)) { ok = 0; break; } /* * XXX: raster array has 4-byte unsigned integer type, that is why * we should rearrange it here. */ #if HOST_BIGENDIAN TIFFSwabArrayOfLong(raster, tile_width * tile_height); #endif /* * For some reason the TIFFReadRGBATile() function chooses the * lower left corner as the origin. Vertically mirror scanlines. */ for( i_row = 0; i_row < tile_height / 2; i_row++ ) { uint32 *top_line, *bottom_line; top_line = raster + tile_width * i_row; bottom_line = raster + tile_width * (tile_height-i_row-1); _TIFFmemcpy(wrk_line, top_line, 4*tile_width); _TIFFmemcpy(top_line, bottom_line, 4*tile_width); _TIFFmemcpy(bottom_line, wrk_line, 4*tile_width); } /* * Write out the result in a tile. */ if( TIFFWriteEncodedTile( out, TIFFComputeTile( out, col, row, 0, 0), raster, 4 * tile_width * tile_height ) == -1 ) { ok = 0; break; } } } _TIFFfree( raster ); _TIFFfree( wrk_line ); return ok; }
/* * Read the next TIFF directory from a file * and convert it to the internal format. * We read directories sequentially. */ int TIFFReadDirectory(TIFF* tif) { register TIFFDirEntry* dp; register int n; register TIFFDirectory* td; TIFFDirEntry* dir; int iv; long v; double dv; const TIFFFieldInfo* fip; int fix; uint16 dircount; toff_t nextdiroff; char* cp; int diroutoforderwarning = 0; tif->tif_diroff = tif->tif_nextdiroff; if (tif->tif_diroff == 0) /* no more directories */ return (0); /* * Cleanup any previous compression state. */ (*tif->tif_cleanup)(tif); tif->tif_curdir++; nextdiroff = 0; if (!isMapped(tif)) { if (!SeekOK(tif, tif->tif_diroff)) { TIFFError(tif->tif_name, "Seek error accessing TIFF directory"); return (0); } if (!ReadOK(tif, &dircount, sizeof (uint16))) { TIFFError(tif->tif_name, "Can not read TIFF directory count"); return (0); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); dir = (TIFFDirEntry *)CheckMalloc(tif, dircount * sizeof (TIFFDirEntry), "to read TIFF directory"); if (dir == NULL) return (0); if (!ReadOK(tif, dir, dircount*sizeof (TIFFDirEntry))) { TIFFError(tif->tif_name, "Can not read TIFF directory"); goto bad; } /* * Read offset to next directory for sequential scans. */ (void) ReadOK(tif, &nextdiroff, sizeof (uint32)); } else { toff_t off = tif->tif_diroff; if (off + sizeof (uint16) > tif->tif_size) { TIFFError(tif->tif_name, "Can not read TIFF directory count"); return (0); } else _TIFFmemcpy(&dircount, tif->tif_base + off, sizeof (uint16)); off += sizeof (uint16); if (tif->tif_flags & TIFF_SWAB) TIFFSwabShort(&dircount); dir = (TIFFDirEntry *)CheckMalloc(tif, dircount * sizeof (TIFFDirEntry), "to read TIFF directory"); if (dir == NULL) return (0); if (off + dircount*sizeof (TIFFDirEntry) > tif->tif_size) { TIFFError(tif->tif_name, "Can not read TIFF directory"); goto bad; } else _TIFFmemcpy(dir, tif->tif_base + off, dircount*sizeof (TIFFDirEntry)); off += dircount* sizeof (TIFFDirEntry); if (off + sizeof (uint32) <= tif->tif_size) _TIFFmemcpy(&nextdiroff, tif->tif_base+off, sizeof (uint32)); } if (tif->tif_flags & TIFF_SWAB) TIFFSwabLong(&nextdiroff); tif->tif_nextdiroff = nextdiroff; tif->tif_flags &= ~TIFF_BEENWRITING; /* reset before new dir */ /* * Setup default value and then make a pass over * the fields to check type and tag information, * and to extract info required to size data * structures. A second pass is made afterwards * to read in everthing not taken in the first pass. */ td = &tif->tif_dir; /* free any old stuff and reinit */ TIFFFreeDirectory(tif); TIFFDefaultDirectory(tif); /* * Electronic Arts writes gray-scale TIFF files * without a PlanarConfiguration directory entry. * Thus we setup a default value here, even though * the TIFF spec says there is no default value. */ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); /* * Sigh, we must make a separate pass through the * directory for the following reason: * * We must process the Compression tag in the first pass * in order to merge in codec-private tag definitions (otherwise * we may get complaints about unknown tags). However, the * Compression tag may be dependent on the SamplesPerPixel * tag value because older TIFF specs permited Compression * to be written as a SamplesPerPixel-count tag entry. * Thus if we don't first figure out the correct SamplesPerPixel * tag value then we may end up ignoring the Compression tag * value because it has an incorrect count value (if the * true value of SamplesPerPixel is not 1). * * It sure would have been nice if Aldus had really thought * this stuff through carefully. */ for (dp = dir, n = dircount; n > 0; n--, dp++) { if (tif->tif_flags & TIFF_SWAB) { TIFFSwabArrayOfShort(&dp->tdir_tag, 2); TIFFSwabArrayOfLong(&dp->tdir_count, 2); } if (dp->tdir_tag == TIFFTAG_SAMPLESPERPIXEL) { if (!TIFFFetchNormalTag(tif, dp)) goto bad; dp->tdir_tag = IGNORE; } } /* * First real pass over the directory. */ fix = 0; for (dp = dir, n = dircount; n > 0; n--, dp++) { /* * Find the field information entry for this tag. * Added check for tags to ignore ... [BFC] */ if( TIFFReassignTagToIgnore(TIS_EXTRACT, dp->tdir_tag) ) dp->tdir_tag = IGNORE; if (dp->tdir_tag == IGNORE) continue; /* * Silicon Beach (at least) writes unordered * directory tags (violating the spec). Handle * it here, but be obnoxious (maybe they'll fix it?). */ if (dp->tdir_tag < tif->tif_fieldinfo[fix]->field_tag) { if (!diroutoforderwarning) { TIFFWarning(tif->tif_name, "invalid TIFF directory; tags are not sorted in ascending order"); diroutoforderwarning = 1; } fix = 0; /* O(n^2) */ } while (fix < tif->tif_nfields && tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag) fix++; if (fix == tif->tif_nfields || tif->tif_fieldinfo[fix]->field_tag != dp->tdir_tag) { TIFFWarning(tif->tif_name, "unknown field with tag %d (0x%x) ignored", dp->tdir_tag, dp->tdir_tag); dp->tdir_tag = IGNORE; fix = 0; /* restart search */ continue; } /* * Null out old tags that we ignore. */ if (tif->tif_fieldinfo[fix]->field_bit == FIELD_IGNORE) { ignore: dp->tdir_tag = IGNORE; continue; } /* * Check data type. */ fip = tif->tif_fieldinfo[fix]; while (dp->tdir_type != (u_short) fip->field_type) { if (fip->field_type == TIFF_ANY) /* wildcard */ break; fip++, fix++; if (fix == tif->tif_nfields || fip->field_tag != dp->tdir_tag) { TIFFWarning(tif->tif_name, "wrong data type %d for \"%s\"; tag ignored", dp->tdir_type, fip[-1].field_name); goto ignore; } } /* * Check count if known in advance. */ if (fip->field_readcount != TIFF_VARIABLE) { uint32 expected = (fip->field_readcount == TIFF_SPP) ? (uint32) td->td_samplesperpixel : (uint32) fip->field_readcount; if (!CheckDirCount(tif, dp, expected)) goto ignore; } switch (dp->tdir_tag) { case TIFFTAG_COMPRESSION: /* * The 5.0 spec says the Compression tag has * one value, while earlier specs say it has * one value per sample. Because of this, we * accept the tag if one value is supplied. */ if (dp->tdir_count == 1) { v = TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset); if (!TIFFSetField(tif, dp->tdir_tag, (int)v)) goto bad; break; } if (!TIFFFetchPerSampleShorts(tif, dp, &iv) || !TIFFSetField(tif, dp->tdir_tag, iv)) goto bad; dp->tdir_tag = IGNORE; break; case TIFFTAG_STRIPOFFSETS: case TIFFTAG_STRIPBYTECOUNTS: case TIFFTAG_TILEOFFSETS: case TIFFTAG_TILEBYTECOUNTS: TIFFSetFieldBit(tif, fip->field_bit); break; case TIFFTAG_IMAGEWIDTH: case TIFFTAG_IMAGELENGTH: case TIFFTAG_IMAGEDEPTH: case TIFFTAG_TILELENGTH: case TIFFTAG_TILEWIDTH: case TIFFTAG_TILEDEPTH: case TIFFTAG_PLANARCONFIG: case TIFFTAG_ROWSPERSTRIP: if (!TIFFFetchNormalTag(tif, dp)) goto bad; dp->tdir_tag = IGNORE; break; case TIFFTAG_EXTRASAMPLES: (void) TIFFFetchExtraSamples(tif, dp); dp->tdir_tag = IGNORE; break; } } /* * Allocate directory structure and setup defaults. */ if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { MissingRequired(tif, "ImageLength"); goto bad; } if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { MissingRequired(tif, "PlanarConfiguration"); goto bad; } /* * Setup appropriate structures (by strip or by tile) */ if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { td->td_nstrips = TIFFNumberOfStrips(tif); td->td_tilewidth = td->td_imagewidth; td->td_tilelength = td->td_rowsperstrip; td->td_tiledepth = td->td_imagedepth; tif->tif_flags &= ~TIFF_ISTILED; } else { td->td_nstrips = TIFFNumberOfTiles(tif); tif->tif_flags |= TIFF_ISTILED; } td->td_stripsperimage = td->td_nstrips; if (td->td_planarconfig == PLANARCONFIG_SEPARATE) td->td_stripsperimage /= td->td_samplesperpixel; if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { MissingRequired(tif, isTiled(tif) ? "TileOffsets" : "StripOffsets"); goto bad; } /* * Second pass: extract other information. */ for (dp = dir, n = dircount; n > 0; n--, dp++) { if (dp->tdir_tag == IGNORE) continue; switch (dp->tdir_tag) { case TIFFTAG_MINSAMPLEVALUE: case TIFFTAG_MAXSAMPLEVALUE: case TIFFTAG_BITSPERSAMPLE: /* * The 5.0 spec says the Compression tag has * one value, while earlier specs say it has * one value per sample. Because of this, we * accept the tag if one value is supplied. * * The MinSampleValue, MaxSampleValue and * BitsPerSample tags are supposed to be written * as one value/sample, but some vendors incorrectly * write one value only -- so we accept that * as well (yech). */ if (dp->tdir_count == 1) { v = TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset); if (!TIFFSetField(tif, dp->tdir_tag, (int)v)) goto bad; break; } /* fall thru... */ case TIFFTAG_DATATYPE: case TIFFTAG_SAMPLEFORMAT: if (!TIFFFetchPerSampleShorts(tif, dp, &iv) || !TIFFSetField(tif, dp->tdir_tag, iv)) goto bad; break; case TIFFTAG_SMINSAMPLEVALUE: case TIFFTAG_SMAXSAMPLEVALUE: if (!TIFFFetchPerSampleAnys(tif, dp, &dv) || !TIFFSetField(tif, dp->tdir_tag, dv)) goto bad; break; case TIFFTAG_STRIPOFFSETS: case TIFFTAG_TILEOFFSETS: if (!TIFFFetchStripThing(tif, dp, td->td_nstrips, &td->td_stripoffset)) goto bad; break; case TIFFTAG_STRIPBYTECOUNTS: case TIFFTAG_TILEBYTECOUNTS: if (!TIFFFetchStripThing(tif, dp, td->td_nstrips, &td->td_stripbytecount)) goto bad; break; case TIFFTAG_COLORMAP: case TIFFTAG_TRANSFERFUNCTION: /* * TransferFunction can have either 1x or 3x data * values; Colormap can have only 3x items. */ v = 1L<<td->td_bitspersample; if (dp->tdir_tag == TIFFTAG_COLORMAP || dp->tdir_count != (uint32) v) { if (!CheckDirCount(tif, dp, (uint32)(3*v))) break; } v *= sizeof (uint16); cp = CheckMalloc(tif, dp->tdir_count * sizeof (uint16), "to read \"TransferFunction\" tag"); if (cp != NULL) { if (TIFFFetchData(tif, dp, cp)) { /* * This deals with there being only * one array to apply to all samples. */ uint32 c = (uint32)1 << td->td_bitspersample; if (dp->tdir_count == c) v = 0; TIFFSetField(tif, dp->tdir_tag, cp, cp+v, cp+2*v); } _TIFFfree(cp); } break; case TIFFTAG_PAGENUMBER: case TIFFTAG_HALFTONEHINTS: case TIFFTAG_YCBCRSUBSAMPLING: case TIFFTAG_DOTRANGE: (void) TIFFFetchShortPair(tif, dp); break; #ifdef COLORIMETRY_SUPPORT case TIFFTAG_REFERENCEBLACKWHITE: (void) TIFFFetchRefBlackWhite(tif, dp); break; #endif /* BEGIN REV 4.0 COMPATIBILITY */ case TIFFTAG_OSUBFILETYPE: v = 0; switch (TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset)) { case OFILETYPE_REDUCEDIMAGE: v = FILETYPE_REDUCEDIMAGE; break; case OFILETYPE_PAGE: v = FILETYPE_PAGE; break; } if (v) (void) TIFFSetField(tif, TIFFTAG_SUBFILETYPE, (int)v); break; /* END REV 4.0 COMPATIBILITY */ default: (void) TIFFFetchNormalTag(tif, dp); break; } } /* * Verify Palette image has a Colormap. */ if (td->td_photometric == PHOTOMETRIC_PALETTE && !TIFFFieldSet(tif, FIELD_COLORMAP)) { MissingRequired(tif, "Colormap"); goto bad; } /* * Attempt to deal with a missing StripByteCounts tag. */ if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { /* * Some manufacturers violate the spec by not giving * the size of the strips. In this case, assume there * is one uncompressed strip of data. */ if ((td->td_planarconfig == PLANARCONFIG_CONTIG && td->td_nstrips > 1) || (td->td_planarconfig == PLANARCONFIG_SEPARATE && td->td_nstrips != td->td_samplesperpixel)) { MissingRequired(tif, "StripByteCounts"); goto bad; } TIFFWarning(tif->tif_name, "TIFF directory is missing required \"%s\" field, calculating from imagelength", _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); EstimateStripByteCounts(tif, dir, dircount); #define BYTECOUNTLOOKSBAD \ ((td->td_stripbytecount[0] == 0 && td->td_stripoffset[0] != 0) || \ (td->td_compression == COMPRESSION_NONE && \ td->td_stripbytecount[0] > TIFFGetFileSize(tif) - td->td_stripoffset[0])) } else if (td->td_nstrips == 1 && BYTECOUNTLOOKSBAD) { /* * Plexus (and others) sometimes give a value * of zero for a tag when they don't know what * the correct value is! Try and handle the * simple case of estimating the size of a one * strip image. */ TIFFWarning(tif->tif_name, "Bogus \"%s\" field, ignoring and calculating from imagelength", _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); EstimateStripByteCounts(tif, dir, dircount); } if (dir) _TIFFfree((char *)dir); if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE)) td->td_maxsamplevalue = (uint16)((1L<<td->td_bitspersample)-1); /* * Setup default compression scheme. */ if (!TIFFFieldSet(tif, FIELD_COMPRESSION)) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); /* * Some manufacturers make life difficult by writing * large amounts of uncompressed data as a single strip. * This is contrary to the recommendations of the spec. * The following makes an attempt at breaking such images * into strips closer to the recommended 8k bytes. A * side effect, however, is that the RowsPerStrip tag * value may be changed. */ if (td->td_nstrips == 1 && td->td_compression == COMPRESSION_NONE && (tif->tif_flags & (TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP) ChopUpSingleUncompressedStrip(tif); /* * Reinitialize i/o since we are starting on a new directory. */ tif->tif_row = (uint32) -1; tif->tif_curstrip = (tstrip_t) -1; tif->tif_col = (uint32) -1; tif->tif_curtile = (ttile_t) -1; tif->tif_tilesize = TIFFTileSize(tif); tif->tif_scanlinesize = TIFFScanlineSize(tif); return (1); bad: if (dir) _TIFFfree(dir); return (0); }
/* * Read the next TIFF directory from a file * and convert it to the internal format. * We read directories sequentially. */ static uint32 ReadDirectory(int fd, unsigned ix, uint32 off) { register TIFFDirEntry *dp; register int n; TIFFDirEntry *dir = 0; uint16 dircount; int space; uint32 nextdiroff = 0; if (off == 0) /* no more directories */ goto done; if (lseek(fd, (off_t) off, 0) != off) { Fatal("Seek error accessing TIFF directory"); goto done; } if (read(fd, (char*) &dircount, sizeof (uint16)) != sizeof (uint16)) { ReadError("directory count"); goto done; } if (swabflag) TIFFSwabShort(&dircount); dir = (TIFFDirEntry *)_TIFFmalloc(dircount * sizeof (TIFFDirEntry)); if (dir == NULL) { Fatal("No space for TIFF directory"); goto done; } n = read(fd, (char*) dir, dircount*sizeof (*dp)); if (n != dircount*sizeof (*dp)) { n /= sizeof (*dp); Error( "Could only read %u of %u entries in directory at offset %#lx", n, dircount, (unsigned long) off); dircount = n; } if (read(fd, (char*) &nextdiroff, sizeof (uint32)) != sizeof (uint32)) nextdiroff = 0; if (swabflag) TIFFSwabLong(&nextdiroff); printf("Directory %u: offset %lu (%#lx) next %lu (%#lx)\n", ix, (unsigned long) off, (unsigned long) off, (unsigned long) nextdiroff, (unsigned long) nextdiroff); for (dp = dir, n = dircount; n > 0; n--, dp++) { if (swabflag) { TIFFSwabArrayOfShort(&dp->tdir_tag, 2); TIFFSwabArrayOfLong(&dp->tdir_count, 2); } PrintTag(stdout, dp->tdir_tag); putchar(' '); PrintType(stdout, dp->tdir_type); putchar(' '); printf("%lu<", (unsigned long) dp->tdir_count); if (dp->tdir_type >= NWIDTHS) { printf(">\n"); continue; } space = dp->tdir_count * datawidth[dp->tdir_type]; if (space <= 4) { switch (dp->tdir_type) { case TIFF_FLOAT: case TIFF_UNDEFINED: case TIFF_ASCII: { unsigned char data[4]; _TIFFmemcpy(data, &dp->tdir_offset, 4); if (swabflag) TIFFSwabLong((uint32*) data); PrintData(stdout, dp->tdir_type, dp->tdir_count, data); break; } case TIFF_BYTE: PrintByte(stdout, bytefmt, dp); break; case TIFF_SBYTE: PrintByte(stdout, sbytefmt, dp); break; case TIFF_SHORT: PrintShort(stdout, shortfmt, dp); break; case TIFF_SSHORT: PrintShort(stdout, sshortfmt, dp); break; case TIFF_LONG: PrintLong(stdout, longfmt, dp); break; case TIFF_SLONG: PrintLong(stdout, slongfmt, dp); break; } } else { unsigned char *data = (unsigned char *)_TIFFmalloc(space); if (data) { if (TIFFFetchData(fd, dp, data)) if (dp->tdir_count > maxitems) { PrintData(stdout, dp->tdir_type, maxitems, data); printf(" ..."); } else PrintData(stdout, dp->tdir_type, dp->tdir_count, data); _TIFFfree(data); } else Error("No space for data for tag %u", dp->tdir_tag); } printf(">\n"); } done: if (dir) _TIFFfree((char *)dir); return (nextdiroff); }
static void TIFFWriteOvrRow( TIFFOvrCache * psCache ) { int nRet, iTileX, iTileY = psCache->nBlockOffset; unsigned char *pabyData; uint32 nBaseDirOffset; uint32 RowsInStrip; /* -------------------------------------------------------------------- */ /* If the output cache is multi-byte per sample, and the file */ /* being written to is of a different byte order than the current */ /* platform, we will need to byte swap the data. */ /* -------------------------------------------------------------------- */ if( TIFFIsByteSwapped(psCache->hTIFF) ) { if( psCache->nBitsPerPixel == 16 ) TIFFSwabArrayOfShort( (uint16 *) psCache->pabyRow1Blocks, (psCache->nBytesPerBlock * psCache->nSamples) / 2 ); else if( psCache->nBitsPerPixel == 32 ) TIFFSwabArrayOfLong( (uint32 *) psCache->pabyRow1Blocks, (psCache->nBytesPerBlock * psCache->nSamples) / 4 ); else if( psCache->nBitsPerPixel == 64 ) TIFFSwabArrayOfDouble( (double *) psCache->pabyRow1Blocks, (psCache->nBytesPerBlock * psCache->nSamples) / 8 ); } /* -------------------------------------------------------------------- */ /* Record original directory position, so we can restore it at */ /* end. */ /* -------------------------------------------------------------------- */ nBaseDirOffset = TIFFCurrentDirOffset( psCache->hTIFF ); nRet = TIFFSetSubDirectory( psCache->hTIFF, psCache->nDirOffset ); assert( nRet == 1 ); /* -------------------------------------------------------------------- */ /* Write blocks to TIFF file. */ /* -------------------------------------------------------------------- */ for( iTileX = 0; iTileX < psCache->nBlocksPerRow; iTileX++ ) { int nTileID; if (psCache->nPlanarConfig == PLANARCONFIG_SEPARATE) { int iSample; for( iSample = 0; iSample < psCache->nSamples; iSample++ ) { pabyData = TIFFGetOvrBlock( psCache, iTileX, iTileY, iSample ); if( psCache->bTiled ) { nTileID = TIFFComputeTile( psCache->hTIFF, iTileX * psCache->nBlockXSize, iTileY * psCache->nBlockYSize, 0, (tsample_t) iSample ); TIFFWriteEncodedTile( psCache->hTIFF, nTileID, pabyData, TIFFTileSize(psCache->hTIFF) ); } else { nTileID = TIFFComputeStrip( psCache->hTIFF, iTileY * psCache->nBlockYSize, (tsample_t) iSample ); RowsInStrip=psCache->nBlockYSize; if ((iTileY+1)*psCache->nBlockYSize>psCache->nYSize) RowsInStrip=psCache->nYSize-iTileY*psCache->nBlockYSize; TIFFWriteEncodedStrip( psCache->hTIFF, nTileID, pabyData, TIFFVStripSize(psCache->hTIFF,RowsInStrip) ); } } } else { pabyData = TIFFGetOvrBlock( psCache, iTileX, iTileY, 0 ); if( psCache->bTiled ) { nTileID = TIFFComputeTile( psCache->hTIFF, iTileX * psCache->nBlockXSize, iTileY * psCache->nBlockYSize, 0, 0 ); TIFFWriteEncodedTile( psCache->hTIFF, nTileID, pabyData, TIFFTileSize(psCache->hTIFF) ); } else { nTileID = TIFFComputeStrip( psCache->hTIFF, iTileY * psCache->nBlockYSize, 0 ); RowsInStrip=psCache->nBlockYSize; if ((iTileY+1)*psCache->nBlockYSize>psCache->nYSize) RowsInStrip=psCache->nYSize-iTileY*psCache->nBlockYSize; TIFFWriteEncodedStrip( psCache->hTIFF, nTileID, pabyData, TIFFVStripSize(psCache->hTIFF,RowsInStrip) ); } } } /* TODO: add checks on error status return of TIFFWriteEncodedTile and TIFFWriteEncodedStrip */ /* -------------------------------------------------------------------- */ /* Rotate buffers. */ /* -------------------------------------------------------------------- */ pabyData = psCache->pabyRow1Blocks; psCache->pabyRow1Blocks = psCache->pabyRow2Blocks; psCache->pabyRow2Blocks = pabyData; _TIFFmemset( pabyData, 0, psCache->nBytesPerRow ); psCache->nBlockOffset++; /* -------------------------------------------------------------------- */ /* Restore access to original directory. */ /* -------------------------------------------------------------------- */ TIFFFlush( psCache->hTIFF ); /* TODO: add checks on error status return of TIFFFlush */ TIFFSetSubDirectory( psCache->hTIFF, nBaseDirOffset ); /* TODO: add checks on error status return of TIFFSetSubDirectory */ }
/* * Write the contents of the current directory * to the specified file. This routine doesn't * handle overwriting a directory with auxiliary * storage that's been changed. */ static int _TIFFWriteDirectory(TIFF* tif, int done) { uint16 dircount; toff_t diroff; ttag_t tag; uint32 nfields; tsize_t dirsize; char* data; TIFFDirEntry* dir; TIFFDirectory* td; u_long b, fields[FIELD_SETLONGS]; int fi, nfi; if (tif->tif_mode == O_RDONLY) return (1); /* * Clear write state so that subsequent images with * different characteristics get the right buffers * setup for them. */ if (done) { if (tif->tif_flags & TIFF_POSTENCODE) { tif->tif_flags &= ~TIFF_POSTENCODE; if (!(*tif->tif_postencode)(tif)) { TIFFError(tif->tif_name, "Error post-encoding before directory write"); return (0); } } (*tif->tif_close)(tif); /* shutdown encoder */ /* * Flush any data that might have been written * by the compression close+cleanup routines. */ if (tif->tif_rawcc > 0 && !TIFFFlushData1(tif)) { TIFFError(tif->tif_name, "Error flushing data before directory write"); return (0); } if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { _TIFFfree(tif->tif_rawdata); tif->tif_rawdata = NULL; tif->tif_rawcc = 0; tif->tif_rawdatasize = 0; } tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP); } td = &tif->tif_dir; /* * Size the directory so that we can calculate * offsets for the data items that aren't kept * in-place in each field. */ nfields = 0; for (b = 0; b <= FIELD_LAST; b++) if (TIFFFieldSet(tif, b) && b != FIELD_CUSTOM) nfields += (b < FIELD_SUBFILETYPE ? 2 : 1); nfields += td->td_customValueCount; dirsize = nfields * sizeof (TIFFDirEntry); data = (char*) _TIFFmalloc(dirsize); if (data == NULL) { TIFFError(tif->tif_name, "Cannot write directory, out of space"); return (0); } /* * Directory hasn't been placed yet, put * it at the end of the file and link it * into the existing directory structure. */ if (tif->tif_diroff == 0 && !TIFFLinkDirectory(tif)) goto bad; tif->tif_dataoff = (toff_t)( tif->tif_diroff + sizeof (uint16) + dirsize + sizeof (toff_t)); if (tif->tif_dataoff & 1) tif->tif_dataoff++; (void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET); tif->tif_curdir++; dir = (TIFFDirEntry*) data; /* * Setup external form of directory * entries and write data items. */ _TIFFmemcpy(fields, td->td_fieldsset, sizeof (fields)); /* * Write out ExtraSamples tag only if * extra samples are present in the data. */ if (FieldSet(fields, FIELD_EXTRASAMPLES) && !td->td_extrasamples) { ResetFieldBit(fields, FIELD_EXTRASAMPLES); nfields--; dirsize -= sizeof (TIFFDirEntry); } /*XXX*/ for (fi = 0, nfi = tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFFieldInfo* fip = tif->tif_fieldinfo[fi]; /* ** For custom fields, we test to see if the custom field ** is set or not. For normal fields, we just use the ** FieldSet test. */ if( fip->field_bit == FIELD_CUSTOM ) { int ci, is_set = FALSE; for( ci = 0; ci < td->td_customValueCount; ci++ ) is_set |= (td->td_customValues[ci].info == fip); if( !is_set ) continue; } else if (!FieldSet(fields, fip->field_bit)) continue; /* ** Handle other fields. */ switch (fip->field_bit) { case FIELD_STRIPOFFSETS: /* * We use one field bit for both strip and tile * offsets, and so must be careful in selecting * the appropriate field descriptor (so that tags * are written in sorted order). */ tag = isTiled(tif) ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS; if (tag != fip->field_tag) continue; if (!TIFFWriteLongArray(tif, TIFF_LONG, tag, dir, (uint32) td->td_nstrips, td->td_stripoffset)) goto bad; break; case FIELD_STRIPBYTECOUNTS: /* * We use one field bit for both strip and tile * byte counts, and so must be careful in selecting * the appropriate field descriptor (so that tags * are written in sorted order). */ tag = isTiled(tif) ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS; if (tag != fip->field_tag) continue; if (!TIFFWriteLongArray(tif, TIFF_LONG, tag, dir, (uint32) td->td_nstrips, td->td_stripbytecount)) goto bad; break; case FIELD_ROWSPERSTRIP: TIFFSetupShortLong(tif, TIFFTAG_ROWSPERSTRIP, dir, td->td_rowsperstrip); break; case FIELD_COLORMAP: if (!TIFFWriteShortTable(tif, TIFFTAG_COLORMAP, dir, 3, td->td_colormap)) goto bad; break; case FIELD_IMAGEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG_IMAGEWIDTH, dir++, td->td_imagewidth); TIFFSetupShortLong(tif, TIFFTAG_IMAGELENGTH, dir, td->td_imagelength); break; case FIELD_TILEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG_TILEWIDTH, dir++, td->td_tilewidth); TIFFSetupShortLong(tif, TIFFTAG_TILELENGTH, dir, td->td_tilelength); break; case FIELD_COMPRESSION: TIFFSetupShort(tif, TIFFTAG_COMPRESSION, dir, td->td_compression); break; case FIELD_PHOTOMETRIC: TIFFSetupShort(tif, TIFFTAG_PHOTOMETRIC, dir, td->td_photometric); break; case FIELD_POSITION: WriteRationalPair(TIFF_RATIONAL, TIFFTAG_XPOSITION, td->td_xposition, TIFFTAG_YPOSITION, td->td_yposition); break; case FIELD_RESOLUTION: WriteRationalPair(TIFF_RATIONAL, TIFFTAG_XRESOLUTION, td->td_xresolution, TIFFTAG_YRESOLUTION, td->td_yresolution); break; case FIELD_BITSPERSAMPLE: case FIELD_MINSAMPLEVALUE: case FIELD_MAXSAMPLEVALUE: case FIELD_SAMPLEFORMAT: if (!TIFFWritePerSampleShorts(tif, fip->field_tag, dir)) goto bad; break; case FIELD_SMINSAMPLEVALUE: case FIELD_SMAXSAMPLEVALUE: if (!TIFFWritePerSampleAnys(tif, _TIFFSampleToTagType(tif), fip->field_tag, dir)) goto bad; break; case FIELD_PAGENUMBER: case FIELD_HALFTONEHINTS: #ifdef YCBCR_SUPPORT case FIELD_YCBCRSUBSAMPLING: #endif #ifdef CMYK_SUPPORT case FIELD_DOTRANGE: #endif if (!TIFFSetupShortPair(tif, fip->field_tag, dir)) goto bad; break; #ifdef CMYK_SUPPORT case FIELD_INKNAMES: if (!TIFFWriteInkNames(tif, dir)) goto bad; break; #endif #ifdef COLORIMETRY_SUPPORT case FIELD_TRANSFERFUNCTION: if (!TIFFWriteTransferFunction(tif, dir)) goto bad; break; #endif #if SUBIFD_SUPPORT case FIELD_SUBIFD: if (!TIFFWriteNormalTag(tif, dir, fip)) goto bad; /* * Total hack: if this directory includes a SubIFD * tag then force the next <n> directories to be * written as ``sub directories'' of this one. This * is used to write things like thumbnails and * image masks that one wants to keep out of the * normal directory linkage access mechanism. */ if (dir->tdir_count > 0) { tif->tif_flags |= TIFF_INSUBIFD; tif->tif_nsubifd = (uint16) dir->tdir_count; if (dir->tdir_count > 1) tif->tif_subifdoff = dir->tdir_offset; else tif->tif_subifdoff = (uint32)( tif->tif_diroff + sizeof (uint16) + ((char*)&dir->tdir_offset-data)); } break; #endif default: if (!TIFFWriteNormalTag(tif, dir, fip)) goto bad; break; } dir++; if( fip->field_bit != FIELD_CUSTOM ) ResetFieldBit(fields, fip->field_bit); } /* * Write directory. */ dircount = (uint16) nfields; diroff = (uint32) tif->tif_nextdiroff; if (tif->tif_flags & TIFF_SWAB) { /* * The file's byte order is opposite to the * native machine architecture. We overwrite * the directory information with impunity * because it'll be released below after we * write it to the file. Note that all the * other tag construction routines assume that * we do this byte-swapping; i.e. they only * byte-swap indirect data. */ for (dir = (TIFFDirEntry*) data; dircount; dir++, dircount--) { TIFFSwabArrayOfShort(&dir->tdir_tag, 2); TIFFSwabArrayOfLong(&dir->tdir_count, 2); } dircount = (uint16) nfields; TIFFSwabShort(&dircount); TIFFSwabLong(&diroff); } (void) TIFFSeekFile(tif, tif->tif_diroff, SEEK_SET); if (!WriteOK(tif, &dircount, sizeof (dircount))) { TIFFError(tif->tif_name, "Error writing directory count"); goto bad; } if (!WriteOK(tif, data, dirsize)) { TIFFError(tif->tif_name, "Error writing directory contents"); goto bad; } if (!WriteOK(tif, &diroff, sizeof (diroff))) { TIFFError(tif->tif_name, "Error writing directory link"); goto bad; } if (done) { TIFFFreeDirectory(tif); tif->tif_flags &= ~TIFF_DIRTYDIRECT; (*tif->tif_cleanup)(tif); /* * Reset directory-related state for subsequent * directories. */ TIFFCreateDirectory(tif); } _TIFFfree(data); return (1); bad: _TIFFfree(data); return (0); }
static int _TIFFWriteCustomDirectory(TIFF* tif, toff_t *pdiroff) { uint16 dircount; uint32 nfields; tsize_t dirsize; char* data; TIFFDirEntry* dir; TIFFDirectory* td; unsigned long b, fields[FIELD_SETLONGS]; int fi, nfi; if (tif->tif_mode == O_RDONLY) return (1); td = &tif->tif_dir; /* * Size the directory so that we can calculate * offsets for the data items that aren't kept * in-place in each field. */ nfields = 0; for (b = 0; b <= FIELD_LAST; b++) if (TIFFFieldSet(tif, b) && b != FIELD_CUSTOM) nfields += (b < FIELD_SUBFILETYPE ? 2 : 1); nfields += td->td_customValueCount; dirsize = nfields * sizeof (TIFFDirEntry); data = (char*) _TIFFmalloc(dirsize); if (data == NULL) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Cannot write directory, out of space"); return (0); } /* * Put the directory at the end of the file. */ tif->tif_diroff = (TIFFSeekFile(tif, (toff_t) 0, SEEK_END)+1) &~ 1; tif->tif_dataoff = (toff_t)( tif->tif_diroff + sizeof (uint16) + dirsize + sizeof (toff_t)); if (tif->tif_dataoff & 1) tif->tif_dataoff++; (void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET); dir = (TIFFDirEntry*) data; /* * Setup external form of directory * entries and write data items. */ _TIFFmemcpy(fields, td->td_fieldsset, sizeof (fields)); for (fi = 0, nfi = tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFFieldInfo* fip = tif->tif_fieldinfo[fi]; /* * For custom fields, we test to see if the custom field * is set or not. For normal fields, we just use the * FieldSet test. */ if( fip->field_bit == FIELD_CUSTOM ) { int ci, is_set = FALSE; for( ci = 0; ci < td->td_customValueCount; ci++ ) is_set |= (td->td_customValues[ci].info == fip); if( !is_set ) continue; } else if (!FieldSet(fields, fip->field_bit)) continue; if( fip->field_bit != FIELD_CUSTOM ) ResetFieldBit(fields, fip->field_bit); } /* * Write directory. */ dircount = (uint16) nfields; *pdiroff = (uint32) tif->tif_nextdiroff; if (tif->tif_flags & TIFF_SWAB) { /* * The file's byte order is opposite to the * native machine architecture. We overwrite * the directory information with impunity * because it'll be released below after we * write it to the file. Note that all the * other tag construction routines assume that * we do this byte-swapping; i.e. they only * byte-swap indirect data. */ for (dir = (TIFFDirEntry*) data; dircount; dir++, dircount--) { TIFFSwabArrayOfShort(&dir->tdir_tag, 2); TIFFSwabArrayOfLong(&dir->tdir_count, 2); } dircount = (uint16) nfields; TIFFSwabShort(&dircount); TIFFSwabLong(pdiroff); } (void) TIFFSeekFile(tif, tif->tif_diroff, SEEK_SET); if (!WriteOK(tif, &dircount, sizeof (dircount))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory count"); goto bad; } if (!WriteOK(tif, data, dirsize)) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory contents"); goto bad; } if (!WriteOK(tif, pdiroff, sizeof (uint32))) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "Error writing directory link"); goto bad; } _TIFFfree(data); return (1); bad: _TIFFfree(data); return (0); }
void TIFFInput::readspec () { uint32 width = 0, height = 0, depth = 0; unsigned short nchans = 1; TIFFGetField (m_tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (m_tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_IMAGEDEPTH, &depth); TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLESPERPIXEL, &nchans); m_spec = ImageSpec ((int)width, (int)height, (int)nchans); float x = 0, y = 0; TIFFGetField (m_tif, TIFFTAG_XPOSITION, &x); TIFFGetField (m_tif, TIFFTAG_YPOSITION, &y); m_spec.x = (int)x; m_spec.y = (int)y; m_spec.z = 0; // FIXME? - TIFF spec describes the positions as in resolutionunit. // What happens if this is not unitless pixels? Are we interpreting // it all wrong? if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &width) == 1 && width > 0) m_spec.full_width = width; if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &height) == 1 && height > 0) m_spec.full_height = height; if (TIFFIsTiled (m_tif)) { TIFFGetField (m_tif, TIFFTAG_TILEWIDTH, &m_spec.tile_width); TIFFGetField (m_tif, TIFFTAG_TILELENGTH, &m_spec.tile_height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_TILEDEPTH, &m_spec.tile_depth); } else { m_spec.tile_width = 0; m_spec.tile_height = 0; m_spec.tile_depth = 0; } m_bitspersample = 8; TIFFGetField (m_tif, TIFFTAG_BITSPERSAMPLE, &m_bitspersample); m_spec.attribute ("oiio:BitsPerSample", (int)m_bitspersample); unsigned short sampleformat = SAMPLEFORMAT_UINT; TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); switch (m_bitspersample) { case 1: case 2: case 4: // Make 1, 2, 4 bpp look like byte images case 8: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT8); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT8); else m_spec.set_format (TypeDesc::UINT8); // punt break; case 16: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT16); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT16); break; case 32: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::FLOAT); break; case 64: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::DOUBLE); break; default: m_spec.set_format (TypeDesc::UNKNOWN); break; } // Use the table for all the obvious things that can be mindlessly // shoved into the image spec. for (int i = 0; tiff_tag_table[i].name; ++i) find_tag (tiff_tag_table[i].tifftag, tiff_tag_table[i].tifftype, tiff_tag_table[i].name); // Now we need to get fields "by hand" for anything else that is less // straightforward... m_photometric = (m_spec.nchannels == 1 ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB); TIFFGetField (m_tif, TIFFTAG_PHOTOMETRIC, &m_photometric); m_spec.attribute ("tiff:PhotometricInterpretation", (int)m_photometric); if (m_photometric == PHOTOMETRIC_PALETTE) { // Read the color map unsigned short *r = NULL, *g = NULL, *b = NULL; TIFFGetField (m_tif, TIFFTAG_COLORMAP, &r, &g, &b); ASSERT (r != NULL && g != NULL && b != NULL); m_colormap.clear (); m_colormap.insert (m_colormap.end(), r, r + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), g, g + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), b, b + (1 << m_bitspersample)); // Palette TIFF images are always 3 channels (to the client) m_spec.nchannels = 3; m_spec.default_channel_names (); } TIFFGetFieldDefaulted (m_tif, TIFFTAG_PLANARCONFIG, &m_planarconfig); m_spec.attribute ("tiff:PlanarConfiguration", (int)m_planarconfig); if (m_planarconfig == PLANARCONFIG_SEPARATE) m_spec.attribute ("planarconfig", "separate"); else m_spec.attribute ("planarconfig", "contig"); int compress = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_COMPRESSION, &compress); m_spec.attribute ("tiff:Compression", compress); switch (compress) { case COMPRESSION_NONE : m_spec.attribute ("compression", "none"); break; case COMPRESSION_LZW : m_spec.attribute ("compression", "lzw"); break; case COMPRESSION_CCITTRLE : m_spec.attribute ("compression", "ccittrle"); break; case COMPRESSION_DEFLATE : case COMPRESSION_ADOBE_DEFLATE : m_spec.attribute ("compression", "zip"); break; case COMPRESSION_PACKBITS : m_spec.attribute ("compression", "packbits"); break; default: break; } int rowsperstrip = -1; if (! m_spec.tile_width) { TIFFGetField (m_tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); if (rowsperstrip > 0) m_spec.attribute ("tiff:RowsPerStrip", rowsperstrip); } // The libtiff docs say that only uncompressed images, or those with // rowsperstrip==1, support random access to scanlines. m_no_random_access = (compress != COMPRESSION_NONE && rowsperstrip != 1); short resunit = -1; TIFFGetField (m_tif, TIFFTAG_RESOLUTIONUNIT, &resunit); switch (resunit) { case RESUNIT_NONE : m_spec.attribute ("ResolutionUnit", "none"); break; case RESUNIT_INCH : m_spec.attribute ("ResolutionUnit", "in"); break; case RESUNIT_CENTIMETER : m_spec.attribute ("ResolutionUnit", "cm"); break; } get_matrix_attribute ("worldtocamera", TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA); get_matrix_attribute ("worldtoscreen", TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN); get_int_attribute ("tiff:subfiletype", TIFFTAG_SUBFILETYPE); // FIXME -- should subfiletype be "conventionized" and used for all // plugins uniformly? // FIXME: do we care about fillorder for 1-bit and 4-bit images? // Special names for shadow maps char *s = NULL; TIFFGetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, &s); if (s) m_emulate_mipmap = true; if (s && ! strcmp (s, "Shadow")) { for (int c = 0; c < m_spec.nchannels; ++c) m_spec.channelnames[c] = "z"; } // N.B. we currently ignore the following TIFF fields: // ExtraSamples // GrayResponseCurve GrayResponseUnit // MaxSampleValue MinSampleValue // NewSubfileType SubfileType(deprecated) // Colorimetry fields // Search for an EXIF IFD in the TIFF file, and if found, rummage // around for Exif fields. #if TIFFLIB_VERSION > 20050912 /* compat with old TIFF libs - skip Exif */ int exifoffset = 0; if (TIFFGetField (m_tif, TIFFTAG_EXIFIFD, &exifoffset) && TIFFReadEXIFDirectory (m_tif, exifoffset)) { for (int i = 0; exif_tag_table[i].name; ++i) find_tag (exif_tag_table[i].tifftag, exif_tag_table[i].tifftype, exif_tag_table[i].name); // I'm not sure what state TIFFReadEXIFDirectory leaves us. // So to be safe, close and re-seek. TIFFClose (m_tif); m_tif = TIFFOpen (m_filename.c_str(), "rm"); TIFFSetDirectory (m_tif, m_subimage); // A few tidbits to look for ImageIOParameter *p; if ((p = m_spec.find_attribute ("Exif:ColorSpace", TypeDesc::INT))) { // Exif spec says that anything other than 0xffff==uncalibrated // should be interpreted to be sRGB. if (*(const int *)p->data() != 0xffff) m_spec.attribute ("oiio::ColorSpace", "sRGB"); } } #endif #if TIFFLIB_VERSION >= 20051230 // Search for IPTC metadata in IIM form -- but older versions of // libtiff botch the size, so ignore it for very old libtiff. int iptcsize = 0; const void *iptcdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_RICHTIFFIPTC, &iptcsize, &iptcdata)) { std::vector<uint32> iptc ((uint32 *)iptcdata, (uint32 *)iptcdata+iptcsize); if (TIFFIsByteSwapped (m_tif)) TIFFSwabArrayOfLong ((uint32*)&iptc[0], iptcsize); decode_iptc_iim (&iptc[0], iptcsize*4, m_spec); } #endif // Search for an XML packet containing XMP (IPTC, Exif, etc.) int xmlsize = 0; const void *xmldata = NULL; if (TIFFGetField (m_tif, TIFFTAG_XMLPACKET, &xmlsize, &xmldata)) { // std::cerr << "Found XML data, size " << xmlsize << "\n"; if (xmldata && xmlsize) { std::string xml ((const char *)xmldata, xmlsize); decode_xmp (xml, m_spec); } } #if 0 // Experimental -- look for photoshop data int photoshopsize = 0; const void *photoshopdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_PHOTOSHOP, &photoshopsize, &photoshopdata)) { std::cerr << "Found PHOTOSHOP data, size " << photoshopsize << "\n"; if (photoshopdata && photoshopsize) { // std::string photoshop ((const char *)photoshopdata, photoshopsize); // std::cerr << "PHOTOSHOP:\n" << photoshop << "\n---\n"; } } #endif }