| // ========================================================== |
| // 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, ¶ms)) { |
| // 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; |
| } |