blob: d677492f79ec90ca6a81cd4939d1fc110ffefd3c [file] [log] [blame]
// ==========================================================
// Metadata functions implementation
// Extended TIFF Directory GEO Tag Support
//
// Design and implementation by
// - Hervé Drolon (drolon@infonie.fr)
// - Thorsten Radde (support@IdealSoftware.com)
// - Berend Engelbrecht (softwarecave@users.sourceforge.net)
// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
//
// Based on the LibTIFF xtiffio sample and on LibGeoTIFF
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================
#ifdef _MSC_VER
#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
#endif
#include "third_party/tiff/libtiff/tiffiop.h"
#include "FreeImage.h"
#include "Utilities.h"
#include "FreeImageTag.h"
#include "FIRational.h"
// ----------------------------------------------------------
// Extended TIFF Directory GEO Tag Support
// ----------------------------------------------------------
/**
Tiff info structure.
Entry format:
{ TAGNUMBER, ReadCount, WriteCount, DataType, FIELDNUM, OkToChange, PassDirCountOnSet, AsciiName }
For ReadCount, WriteCount, -1 = unknown.
*/
static const TIFFFieldInfo xtiffFieldInfo[] = {
{ TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoPixelScale" },
{ TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"Intergraph TransformationMatrix" },
{ TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTransformationMatrix" },
{ TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTiePoints" },
{ TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoKeyDirectory" },
{ TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoDoubleParams" },
{ TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, (char*) "GeoASCIIParams" },
{ TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, (char*)"JPL Carto IFD offset" } /** Don't use this! **/
};
static void
_XTIFFLocalDefaultDirectory(TIFF *tif) {
int tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
// Install the extended Tag field info
TIFFMergeFieldInfo(tif, xtiffFieldInfo, tag_size);
}
static TIFFExtendProc _ParentExtender;
/**
This is the callback procedure, and is
called by the DefaultDirectory method
every time a new TIFF directory is opened.
*/
static void
_XTIFFDefaultDirectory(TIFF *tif) {
// set up our own defaults
_XTIFFLocalDefaultDirectory(tif);
/*
Since an XTIFF client module may have overridden
the default directory method, we call it now to
allow it to set up the rest of its own methods.
*/
if (_ParentExtender) {
(*_ParentExtender)(tif);
}
}
/**
XTIFF Initializer -- sets up the callback procedure for the TIFF module.
@see PluginTIFF::InitTIFF
*/
void
XTIFFInitialize(void) {
static int first_time = 1;
if (! first_time) {
return; /* Been there. Done that. */
}
first_time = 0;
// Grab the inherited method and install
_ParentExtender = TIFFSetTagExtender(_XTIFFDefaultDirectory);
}
// ----------------------------------------------------------
// GeoTIFF tag reading / writing
// ----------------------------------------------------------
BOOL
tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
char defaultKey[16];
// first check for a mandatory tag
{
short tag_count = 0;
void* data = NULL;
if(!TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tag_count, &data)) {
// no GeoTIFF tag here
return TRUE;
}
}
// next, read GeoTIFF tags
const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
TagLib& tag_lib = TagLib::instance();
for(size_t i = 0; i < tag_size; i++) {
const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
if(fieldInfo->field_type == TIFF_ASCII) {
char *params = NULL;
if(TIFFGetField(tif, fieldInfo->field_tag, &params)) {
// create a tag
FITAG *tag = FreeImage_CreateTag();
if(!tag) {
return FALSE;
}
WORD tag_id = (WORD)fieldInfo->field_tag;
FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)fieldInfo->field_type);
FreeImage_SetTagID(tag, tag_id);
FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey));
FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id));
FreeImage_SetTagLength(tag, (DWORD)strlen(params) + 1);
FreeImage_SetTagCount(tag, FreeImage_GetTagLength(tag));
FreeImage_SetTagValue(tag, params);
FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag);
// delete the tag
FreeImage_DeleteTag(tag);
}
} else {
short tag_count = 0;
void* data = NULL;
if(TIFFGetField(tif, fieldInfo->field_tag, &tag_count, &data)) {
// create a tag
FITAG *tag = FreeImage_CreateTag();
if(!tag) {
return FALSE;
}
WORD tag_id = (WORD)fieldInfo->field_tag;
FREE_IMAGE_MDTYPE tag_type = (FREE_IMAGE_MDTYPE)fieldInfo->field_type;
FreeImage_SetTagType(tag, tag_type);
FreeImage_SetTagID(tag, tag_id);
FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey));
FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id));
FreeImage_SetTagLength(tag, FreeImage_TagDataWidth(tag_type) * tag_count);
FreeImage_SetTagCount(tag, tag_count);
FreeImage_SetTagValue(tag, data);
FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag);
// delete the tag
FreeImage_DeleteTag(tag);
}
}
} // for(tag_size)
return TRUE;
}
BOOL
tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
char defaultKey[16];
if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) {
// no GeoTIFF tag here
return TRUE;
}
const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
TagLib& tag_lib = TagLib::instance();
for(size_t i = 0; i < tag_size; i++) {
const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
FITAG *tag = NULL;
const char *key = tag_lib.getTagFieldName(TagLib::GEOTIFF, (WORD)fieldInfo->field_tag, defaultKey);
if(FreeImage_GetMetadata(FIMD_GEOTIFF, dib, key, &tag)) {
if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagValue(tag));
} else {
TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
}
}
}
return TRUE;
}
// ----------------------------------------------------------
// TIFF EXIF tag reading & writing
// ----------------------------------------------------------
/**
Read a single Exif tag
@param tif TIFF handle
@param tag_id TIFF Tag ID
@param dib Image being read
@param md_model Metadata model where to store the tag
@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL
tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) {
uint32 value_count = 0;
int mem_alloc = 0;
void *raw_data = NULL;
if(tag_id == TIFFTAG_EXIFIFD) {
// Exif IFD offset - skip this tag
// md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model
return TRUE;
}
if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) {
// Exif GPS IFD offset - skip this tag
// should be processed in another way ...
return TRUE;
}
TagLib& tagLib = TagLib::instance();
// get the tag key - use NULL to avoid reading GeoTIFF tags
const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL);
if(key == NULL) {
return TRUE;
}
const TIFFField *fip = TIFFFieldWithTag(tif, tag_id);
if(fip == NULL) {
return TRUE;
}
if(TIFFFieldPassCount(fip)) {
// a count value is required for 'TIFFGetField'
if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) {
// a count is required, it will be of type uint16
uint16 value_count16 = 0;
if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) {
// stop, ignore error
return TRUE;
}
value_count = value_count16;
} else {
// a count is required, it will be of type uint32
uint32 value_count32 = 0;
if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) {
// stop, ignore error
return TRUE;
}
value_count = value_count32;
}
} else {
// determine count
if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) {
value_count = 1;
} else if (TIFFFieldReadCount(fip) == TIFF_SPP) {
uint16 spp;
TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
value_count = spp;
} else {
value_count = TIFFFieldReadCount(fip);
}
// access fields as pointers to data
// (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code)
if(TIFFFieldTag(fip) == TIFFTAG_TRANSFERFUNCTION) {
// reading this tag cause a bug probably located somewhere inside libtiff
return TRUE;
}
if ((TIFFFieldDataType(fip) == TIFF_ASCII
|| TIFFFieldReadCount(fip) == TIFF_VARIABLE
|| TIFFFieldReadCount(fip) == TIFF_VARIABLE2
|| TIFFFieldReadCount(fip) == TIFF_SPP
|| value_count > 1)
&& TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER
&& TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS
&& TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING
&& TIFFFieldTag(fip) != TIFFTAG_DOTRANGE
&& TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE //<- these two are tricky -
&& TIFFFieldTag(fip) != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value
) {
if(TIFFGetField(tif, tag_id, &raw_data) != 1) {
// stop, ignore error
return TRUE;
}
} else {
int value_size = 0;
// access fields as values
// Note:
// For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals.
{
TIFFDataType tag_type = TIFFFieldDataType(fip);
switch(tag_type) {
case TIFF_RATIONAL:
case TIFF_SRATIONAL:
value_size = 4;
break;
default:
value_size = TIFFDataWidth(tag_type);
break;
}
}
raw_data = _TIFFmalloc(value_size * value_count);
mem_alloc = 1;
int ok = FALSE;
// ### if value_count > 1, tag is PAGENUMBER or HALFTONEHINTS or YCBCRSUBSAMPLING or DOTRANGE,
// all off which are value_count == 2 (see tif_dirinfo.c)
switch(value_count)
{
case 1:
ok = TIFFGetField(tif, tag_id, raw_data);
break;
case 2:
ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1);
break;
/* # we might need more in the future:
case 3:
ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2);
break;
*/
default:
FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip));
break;
}
if(ok != 1) {
_TIFFfree(raw_data);
return TRUE;
}
}
}
// build FreeImage tag from Tiff Tag data we collected
FITAG *fitag = FreeImage_CreateTag();
if(!fitag) {
if(mem_alloc) {
_TIFFfree(raw_data);
}
return FALSE;
}
FreeImage_SetTagID(fitag, (WORD)tag_id);
FreeImage_SetTagKey(fitag, key);
switch(TIFFFieldDataType(fip)) {
case TIFF_BYTE:
FreeImage_SetTagType(fitag, FIDT_BYTE);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_UNDEFINED:
FreeImage_SetTagType(fitag, FIDT_UNDEFINED);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SBYTE:
FreeImage_SetTagType(fitag, FIDT_SBYTE);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SHORT:
FreeImage_SetTagType(fitag, FIDT_SHORT);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SSHORT:
FreeImage_SetTagType(fitag, FIDT_SSHORT);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_LONG:
FreeImage_SetTagType(fitag, FIDT_LONG);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_IFD:
FreeImage_SetTagType(fitag, FIDT_IFD);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SLONG:
FreeImage_SetTagType(fitag, FIDT_SLONG);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_RATIONAL: {
// LibTIFF converts rational to floats : reconvert floats to rationals
DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD));
for(uint32 i = 0; i < value_count; i++) {
float *fv = (float*)raw_data;
FIRational rational(fv[i]);
rvalue[2*i] = rational.getNumerator();
rvalue[2*i+1] = rational.getDenominator();
}
FreeImage_SetTagType(fitag, FIDT_RATIONAL);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, rvalue);
free(rvalue);
}
break;
case TIFF_SRATIONAL: {
// LibTIFF converts rational to floats : reconvert floats to rationals
LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG));
for(uint32 i = 0; i < value_count; i++) {
float *fv = (float*)raw_data;
FIRational rational(fv[i]);
rvalue[2*i] = rational.getNumerator();
rvalue[2*i+1] = rational.getDenominator();
}
FreeImage_SetTagType(fitag, FIDT_RATIONAL);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, rvalue);
free(rvalue);
}
break;
case TIFF_FLOAT:
FreeImage_SetTagType(fitag, FIDT_FLOAT);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_DOUBLE:
FreeImage_SetTagType(fitag, FIDT_DOUBLE);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_LONG8: // BigTIFF 64-bit unsigned integer
FreeImage_SetTagType(fitag, FIDT_LONG8);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset)
FreeImage_SetTagType(fitag, FIDT_IFD8);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SLONG8: // BigTIFF 64-bit signed integer
FreeImage_SetTagType(fitag, FIDT_SLONG8);
FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_ASCII:
default: {
size_t length = 0;
if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) {
// when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE),
// tiff_read_exif_tag function gives length of 1 so all strings are truncated ...
// ... try to avoid this by using an explicit calculation for 'length'
length = strlen((char*)raw_data) + 1;
}
else {
// remember that raw_data = _TIFFmalloc(value_size * value_count);
const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) );
length = value_size * value_count;
}
FreeImage_SetTagType(fitag, FIDT_ASCII);
FreeImage_SetTagLength(fitag, (DWORD)length);
FreeImage_SetTagCount(fitag, (DWORD)length);
FreeImage_SetTagValue(fitag, raw_data);
}
break;
}
const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id);
if(description) {
FreeImage_SetTagDescription(fitag, description);
}
// store the tag
FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag);
// destroy the tag
FreeImage_DeleteTag(fitag);
if(mem_alloc) {
_TIFFfree(raw_data);
}
return TRUE;
}
/**
Read all known exif tags
@param tif TIFF handle
@param md_model Metadata model where to store the tags
@param dib Image being read
@return Returns TRUE if successful, returns FALSE otherwise
*/
BOOL
tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
TagLib& tagLib = TagLib::instance();
const int count = TIFFGetTagListCount(tif);
for(int i = 0; i < count; i++) {
uint32 tag_id = TIFFGetTagListEntry(tif, i);
// read the tag
if (!tiff_read_exif_tag(tif, tag_id, dib, md_model))
return FALSE;
}
// we want to know values of standard tags too!!
// loop over all Core Directory Tags
// ### uses private data, but there is no other way
if(md_model == TagLib::EXIF_MAIN) {
const TIFFDirectory *td = &tif->tif_dir;
uint32 lastTag = 0; //<- used to prevent reading some tags twice (as stored in tif_fieldinfo)
for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
const TIFFField *fld = tif->tif_fields[fi];
const uint32 tag_id = TIFFFieldTag(fld);
if(tag_id == lastTag) {
continue;
}
// test if tag value is set
// (lifted directly from LibTiff _TIFFWriteDirectory)
if( fld->field_bit == FIELD_CUSTOM ) {
int is_set = FALSE;
for(int ci = 0; ci < td->td_customValueCount; ci++ ) {
is_set |= (td->td_customValues[ci].info == fld);
}
if( !is_set ) {
continue;
}
} else if(!TIFFFieldSet(tif, fld->field_bit)) {
continue;
}
// process *all* other tags (some will be ignored)
tiff_read_exif_tag(tif, tag_id, dib, md_model);
lastTag = tag_id;
}
}
return TRUE;
}
/**
Skip tags that are already handled by the LibTIFF writing process
*/
static BOOL
skip_write_field(TIFF* tif, uint32 tag) {
switch (tag) {
case TIFFTAG_SUBFILETYPE:
case TIFFTAG_OSUBFILETYPE:
case TIFFTAG_IMAGEWIDTH:
case TIFFTAG_IMAGELENGTH:
case TIFFTAG_BITSPERSAMPLE:
case TIFFTAG_COMPRESSION:
case TIFFTAG_PHOTOMETRIC:
case TIFFTAG_THRESHHOLDING:
case TIFFTAG_CELLWIDTH:
case TIFFTAG_CELLLENGTH:
case TIFFTAG_FILLORDER:
case TIFFTAG_STRIPOFFSETS:
case TIFFTAG_ORIENTATION:
case TIFFTAG_SAMPLESPERPIXEL:
case TIFFTAG_ROWSPERSTRIP:
case TIFFTAG_STRIPBYTECOUNTS:
case TIFFTAG_MINSAMPLEVALUE:
case TIFFTAG_MAXSAMPLEVALUE:
case TIFFTAG_XRESOLUTION:
case TIFFTAG_YRESOLUTION:
case TIFFTAG_PLANARCONFIG:
case TIFFTAG_FREEOFFSETS:
case TIFFTAG_FREEBYTECOUNTS:
case TIFFTAG_GRAYRESPONSEUNIT:
case TIFFTAG_GRAYRESPONSECURVE:
case TIFFTAG_GROUP3OPTIONS:
case TIFFTAG_GROUP4OPTIONS:
case TIFFTAG_RESOLUTIONUNIT:
case TIFFTAG_PAGENUMBER:
case TIFFTAG_COLORRESPONSEUNIT:
case TIFFTAG_PREDICTOR:
case TIFFTAG_COLORMAP:
case TIFFTAG_HALFTONEHINTS:
case TIFFTAG_TILEWIDTH:
case TIFFTAG_TILELENGTH:
case TIFFTAG_TILEOFFSETS:
case TIFFTAG_TILEBYTECOUNTS:
case TIFFTAG_EXTRASAMPLES:
case TIFFTAG_SAMPLEFORMAT:
case TIFFTAG_SMINSAMPLEVALUE:
case TIFFTAG_SMAXSAMPLEVALUE:
// skip always, values have been set in SaveOneTIFF()
return TRUE;
break;
case TIFFTAG_RICHTIFFIPTC:
// skip always, IPTC metadata model is set in tiff_write_iptc_profile()
return TRUE;
break;
case TIFFTAG_YCBCRCOEFFICIENTS:
case TIFFTAG_REFERENCEBLACKWHITE:
case TIFFTAG_YCBCRSUBSAMPLING:
// skip as they cannot be filled yet
return TRUE;
break;
case TIFFTAG_PAGENAME:
{
char *value = NULL;
TIFFGetField(tif, TIFFTAG_PAGENAME, &value);
// only skip if no value has been set
if(value == NULL) {
return FALSE;
} else {
return TRUE;
}
}
default:
return FALSE;
break;
}
}
/**
Write all known exif tags
@param tif TIFF handle
@param md_model Metadata model from where to load the tags
@param dib Image being written
@return Returns TRUE if successful, returns FALSE otherwise
*/
BOOL
tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
char defaultKey[16];
// only EXIF_MAIN so far
if(md_model != TagLib::EXIF_MAIN) {
return FALSE;
}
if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib) == 0) {
return FALSE;
}
TagLib& tag_lib = TagLib::instance();
for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
const TIFFField *fld = tif->tif_fields[fi];
const uint32 tag_id = TIFFFieldTag(fld);
if(skip_write_field(tif, tag_id)) {
// skip tags that are already handled by the LibTIFF writing process
continue;
}
FITAG *tag = NULL;
// get the tag key
const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey);
if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
TIFFDataType tif_tag_type = TIFFFieldDataType(fld);
// check for identical formats
// (enum value are the sames between FREE_IMAGE_MDTYPE and TIFFDataType types)
if((int)tif_tag_type != (int)tag_type) {
// skip tag or _TIFFmemcpy will fail
continue;
}
// type of storage may differ (e.g. rationnal array vs float array type)
if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) {
// skip tag or _TIFFmemcpy will fail
continue;
}
if(tag_type == FIDT_ASCII) {
TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag));
} else {
TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
}
}
}
return TRUE;
}