From d2d798d0f11abcbf141db69869e76e8d123ec1eb Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 13 Dec 2014 20:28:24 +0000 Subject: FreeImage updated to 3.16 git-svn-id: http://svn.miranda-ng.org/main/trunk@11379 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AdvaImg/src/Metadata/Exif.cpp | 158 +- plugins/AdvaImg/src/Metadata/FreeImageTag.cpp | 26 +- plugins/AdvaImg/src/Metadata/FreeImageTag.h | 23 +- plugins/AdvaImg/src/Metadata/IPTC.cpp | 2 +- plugins/AdvaImg/src/Metadata/TagConversion.cpp | 2188 ++++++++++++------------ plugins/AdvaImg/src/Metadata/XTIFF.cpp | 1340 ++++++++------- 6 files changed, 1947 insertions(+), 1790 deletions(-) (limited to 'plugins/AdvaImg/src/Metadata') diff --git a/plugins/AdvaImg/src/Metadata/Exif.cpp b/plugins/AdvaImg/src/Metadata/Exif.cpp index ed667b0974..abc840607b 100644 --- a/plugins/AdvaImg/src/Metadata/Exif.cpp +++ b/plugins/AdvaImg/src/Metadata/Exif.cpp @@ -515,17 +515,18 @@ processExifTag(FIBITMAP *dib, FITAG *tag, char *pval, BOOL msb_order, TagLib::MD } /** - Process Exif directory - - @param dib Input FIBITMAP - @param tiffp Pointer to the TIFF header - @param offset 0th IFD offset - @param length Length of the datafile - @param msb_order Endianess order of the datafile - @return +Process Exif directory + +@param dib Input FIBITMAP +@param tiffp Pointer to the TIFF header +@param offset 0th IFD offset +@param length Length of the datafile +@param msb_order Endianess order of the datafile +@param starting_md_model Metadata model of the IFD (should be TagLib::EXIF_MAIN for a jpeg) +@return Returns TRUE if sucessful, returns FALSE otherwise */ static BOOL -jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) { +jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order, TagLib::MDMODEL starting_md_model) { WORD de, nde; std::stack destack; // directory entries stack @@ -548,7 +549,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // set the metadata model to Exif - TagLib::MDMODEL md_model = TagLib::EXIF_MAIN; + TagLib::MDMODEL md_model = starting_md_model; // set the pointer to the first IFD (0th IFD) and follow it were it leads. @@ -787,6 +788,8 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig return TRUE; } +// -------------------------------------------------------------------------- + /** Read and decode JPEG_APP1 marker (Exif profile) @param dib Input FIBITMAP @@ -795,7 +798,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig @return Returns TRUE if successful, FALSE otherwise */ BOOL -jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { +jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) { // marker identifying string for Exif = "Exif\0\0" BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; BYTE lsb_first[4] = { 0x49, 0x49, 0x2A, 0x00 }; // Intel order @@ -849,11 +852,140 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) } */ - // process Exif directories - return jpeg_read_exif_dir(dib, profile, first_offset, length, bMotorolaOrder); + // process Exif directories, starting with Exif-TIFF IFD + return jpeg_read_exif_dir(dib, profile, first_offset, length, bMotorolaOrder, TagLib::EXIF_MAIN); } return FALSE; } +/** + Read JPEG_APP1 marker (Exif profile) + @param dib Input FIBITMAP + @param dataptr Pointer to the APP1 marker + @param datalen APP1 marker length + @return Returns TRUE if successful, FALSE otherwise +*/ +BOOL +jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length) { + // marker identifying string for Exif = "Exif\0\0" + BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; + + // verify the identifying string + if(memcmp(exif_signature, profile, sizeof(exif_signature)) != 0) { + // not an Exif profile + return FALSE; + } + + // create a tag + FITAG *tag = FreeImage_CreateTag(); + if(tag) { + FreeImage_SetTagKey(tag, g_TagLib_ExifRawFieldName); + FreeImage_SetTagLength(tag, (DWORD)length); + FreeImage_SetTagCount(tag, (DWORD)length); + FreeImage_SetTagType(tag, FIDT_BYTE); + FreeImage_SetTagValue(tag, profile); + + // store the tag + FreeImage_SetMetadata(FIMD_EXIF_RAW, dib, FreeImage_GetTagKey(tag), tag); + + // destroy the tag + FreeImage_DeleteTag(tag); + + return TRUE; + } + return FALSE; +} + +/** +Read and decode JPEG-XR Exif IFD +@param dib Input FIBITMAP +@param profile Pointer to the Exif marker +@param length Exif marker length +@return Returns TRUE if successful, FALSE otherwise +*/ +BOOL +jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) { + // assume Little Endian order + BOOL bMotorolaOrder = FALSE; + + // process Exif specific IFD + return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_EXIF); +} + +/** +Read and decode JPEG-XR Exif-GPS IFD +@param dib Input FIBITMAP +@param profile Pointer to the Exif-GPS marker +@param length Exif-GPS marker length +@return Returns TRUE if successful, FALSE otherwise +*/ +BOOL +jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) { + // assume Little Endian order + BOOL bMotorolaOrder = FALSE; + + // process Exif GPS IFD + return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_GPS); +} + +/** +Rotate a dib according to Exif info +@param dib Input / Output dib to rotate +@see PluginJPEG.cpp +*/ +void +RotateExif(FIBITMAP **dib) { + // check for Exif rotation + if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, *dib)) { + FIBITMAP *rotated = NULL; + // process Exif rotation + FITAG *tag = NULL; + FreeImage_GetMetadata(FIMD_EXIF_MAIN, *dib, "Orientation", &tag); + if(tag != NULL) { + if(FreeImage_GetTagID(tag) == TAG_ORIENTATION) { + unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (orientation) { + case 1: // "top, left side" => 0° + break; + case 2: // "top, right side" => flip left-right + FreeImage_FlipHorizontal(*dib); + break; + case 3: // "bottom, right side"; => -180° + rotated = FreeImage_Rotate(*dib, 180); + FreeImage_Unload(*dib); + *dib = rotated; + break; + case 4: // "bottom, left side" => flip up-down + FreeImage_FlipVertical(*dib); + break; + case 5: // "left side, top" => +90° + flip up-down + rotated = FreeImage_Rotate(*dib, 90); + FreeImage_Unload(*dib); + *dib = rotated; + FreeImage_FlipVertical(*dib); + break; + case 6: // "right side, top" => -90° + rotated = FreeImage_Rotate(*dib, -90); + FreeImage_Unload(*dib); + *dib = rotated; + break; + case 7: // "right side, bottom" => -90° + flip up-down + rotated = FreeImage_Rotate(*dib, -90); + FreeImage_Unload(*dib); + *dib = rotated; + FreeImage_FlipVertical(*dib); + break; + case 8: // "left side, bottom" => +90° + rotated = FreeImage_Rotate(*dib, 90); + FreeImage_Unload(*dib); + *dib = rotated; + break; + default: + break; + } + } + } + } +} diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp index 5d9f3034ec..f8af31f9eb 100644 --- a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp +++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp @@ -117,11 +117,23 @@ FreeImage_CloneTag(FITAG *tag) { // tag length dst_tag->length = src_tag->length; // tag value - dst_tag->value = (BYTE*)malloc(src_tag->length * sizeof(BYTE)); - if(!dst_tag->value) { - throw FI_MSG_ERROR_MEMORY; + switch(dst_tag->type) { + case FIDT_ASCII: + dst_tag->value = (BYTE*)malloc((src_tag->length + 1) * sizeof(BYTE)); + if(!dst_tag->value) { + throw FI_MSG_ERROR_MEMORY; + } + memcpy(dst_tag->value, src_tag->value, src_tag->length); + ((BYTE*)dst_tag->value)[src_tag->length] = 0; + break; + default: + dst_tag->value = (BYTE*)malloc(src_tag->length * sizeof(BYTE)); + if(!dst_tag->value) { + throw FI_MSG_ERROR_MEMORY; + } + memcpy(dst_tag->value, src_tag->value, src_tag->length); + break; } - memcpy(dst_tag->value, src_tag->value, src_tag->length); return clone; @@ -305,9 +317,9 @@ FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) { 4, // FIDT_FLOAT = 11, // 32-bit IEEE floating point 8, // FIDT_DOUBLE = 12, // 64-bit IEEE floating point 4, // FIDT_IFD = 13, // 32-bit unsigned integer (offset) - 4, // FIDT_PALETTE = 14 // 32-bit RGBQUAD - 0, // placeholder (15) - 8, // FIDT_LONG8 = 16, // 64-bit unsigned integer + 4, // FIDT_PALETTE = 14 // 32-bit RGBQUAD + 0, // placeholder (15) + 8, // FIDT_LONG8 = 16, // 64-bit unsigned integer 8, // FIDT_SLONG8 = 17, // 64-bit signed integer 8 // FIDT_IFD8 = 18 // 64-bit unsigned integer (offset) }; diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.h b/plugins/AdvaImg/src/Metadata/FreeImageTag.h index 4f3bffa1ce..f9021ace27 100644 --- a/plugins/AdvaImg/src/Metadata/FreeImageTag.h +++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.h @@ -321,15 +321,15 @@ TagInfo *tag_info = s.getTagInfo(EXIF_MAIN, 0x0100); Note on multi-threaded applications : - -The singleton pattern must be carefully constructed in multi-threaded applications. -If two threads are to execute the creation method at the same time when a singleton -does not yet exist, they both must check for an instance of the singleton and then -only one should create the new one. -The classic solution to this problem is to use mutual exclusion on the class that -indicates that the object is being instantiated. -The FreeImage solution is to instantiate the singleton before any other thread is launched, -i.e. inside the FreeImage_Initialise function (see Plugin.cpp). + +The singleton pattern must be carefully constructed in multi-threaded applications. +If two threads are to execute the creation method at the same time when a singleton +does not yet exist, they both must check for an instance of the singleton and then +only one should create the new one. +The classic solution to this problem is to use mutual exclusion on the class that +indicates that the object is being instantiated. +The FreeImage solution is to instantiate the singleton before any other thread is launched, +i.e. inside the FreeImage_Initialise function (see Plugin.cpp). */ class TagLib { @@ -468,7 +468,10 @@ extern "C" { #endif // JPEG Exif profile -BOOL jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen); +BOOL jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen); +BOOL jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length); +BOOL jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length); +BOOL jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length); // JPEG / TIFF IPTC profile BOOL read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen); diff --git a/plugins/AdvaImg/src/Metadata/IPTC.cpp b/plugins/AdvaImg/src/Metadata/IPTC.cpp index 6f28c486b3..bde718c986 100644 --- a/plugins/AdvaImg/src/Metadata/IPTC.cpp +++ b/plugins/AdvaImg/src/Metadata/IPTC.cpp @@ -91,7 +91,7 @@ read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { offset++; int directoryType = profile[offset++]; - int tagType = profile[offset++]; + int tagType = profile[offset++];; int tagByteCount = ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF); offset += 2; diff --git a/plugins/AdvaImg/src/Metadata/TagConversion.cpp b/plugins/AdvaImg/src/Metadata/TagConversion.cpp index 06a1970cd8..f05818600c 100644 --- a/plugins/AdvaImg/src/Metadata/TagConversion.cpp +++ b/plugins/AdvaImg/src/Metadata/TagConversion.cpp @@ -1,1094 +1,1094 @@ -// ========================================================== -// Tag to string conversion functions -// -// Design and implementation by -// - Hervé Drolon -// -// 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 "FreeImage.h" -#include "Utilities.h" -#include "FreeImageTag.h" -#include "FIRational.h" - -#define MAX_TEXT_EXTENT 512 - -/** -Convert a tag to a C string -*/ -static const char* -ConvertAnyTag(FITAG *tag) { - char format[MAX_TEXT_EXTENT]; - static std::string buffer; - DWORD i; - - if(!tag) - return NULL; - - buffer.erase(); - - // convert the tag value to a string buffer - - FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); - DWORD tag_count = FreeImage_GetTagCount(tag); - - switch(tag_type) { - case FIDT_BYTE: // N x 8-bit unsigned integer - { - BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld", (LONG) pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %ld", (LONG) pvalue[i]); - buffer += format; - } - break; - } - case FIDT_SHORT: // N x 16-bit unsigned integer - { - unsigned short *pvalue = (unsigned short *)FreeImage_GetTagValue(tag); - - sprintf(format, "%hu", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %hu", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_LONG: // N x 32-bit unsigned integer - { - DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag); - - sprintf(format, "%lu", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %lu", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_RATIONAL: // N x 64-bit unsigned fraction - { - DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]); - buffer += format; - } - break; - } - case FIDT_SBYTE: // N x 8-bit signed integer - { - char *pvalue = (char*)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld", (LONG) pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %ld", (LONG) pvalue[i]); - buffer += format; - } - break; - } - case FIDT_SSHORT: // N x 16-bit signed integer - { - short *pvalue = (short *)FreeImage_GetTagValue(tag); - - sprintf(format, "%hd", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %hd", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_SLONG: // N x 32-bit signed integer - { - LONG *pvalue = (LONG *)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %ld", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_SRATIONAL:// N x 64-bit signed fraction - { - LONG *pvalue = (LONG*)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]); - buffer += format; - } - break; - } - case FIDT_FLOAT: // N x 32-bit IEEE floating point - { - float *pvalue = (float *)FreeImage_GetTagValue(tag); - - sprintf(format, "%f", (double) pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, "%f", (double) pvalue[i]); - buffer += format; - } - break; - } - case FIDT_DOUBLE: // N x 64-bit IEEE floating point - { - double *pvalue = (double *)FreeImage_GetTagValue(tag); - - sprintf(format, "%f", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, "%f", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_IFD: // N x 32-bit unsigned integer (offset) - { - DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag); - - sprintf(format, "%X", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " %X", pvalue[i]); - buffer += format; - } - break; - } - case FIDT_PALETTE: // N x 32-bit RGBQUAD - { - RGBQUAD *pvalue = (RGBQUAD *)FreeImage_GetTagValue(tag); - - sprintf(format, "(%d,%d,%d,%d)", pvalue[0].rgbRed, pvalue[0].rgbGreen, pvalue[0].rgbBlue, pvalue[0].rgbReserved); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, " (%d,%d,%d,%d)", pvalue[i].rgbRed, pvalue[i].rgbGreen, pvalue[i].rgbBlue, pvalue[i].rgbReserved); - buffer += format; - } - break; - } - - case FIDT_LONG8: // N x 64-bit unsigned integer - { - UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, "%ld", pvalue[i]); - buffer += format; - } - break; - } - - case FIDT_IFD8: // N x 64-bit unsigned integer (offset) - { - UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag); - - sprintf(format, "%X", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, "%X", pvalue[i]); - buffer += format; - } - break; - } - - case FIDT_SLONG8: // N x 64-bit signed integer - { - INT64 *pvalue = (INT64 *)FreeImage_GetTagValue(tag); - - sprintf(format, "%ld", pvalue[0]); - buffer += format; - for(i = 1; i < tag_count; i++) { - sprintf(format, "%ld", pvalue[i]); - buffer += format; - } - break; - } - - case FIDT_ASCII: // 8-bit bytes w/ last byte null - case FIDT_UNDEFINED:// 8-bit untyped data - default: - { - int max_size = MIN((int)FreeImage_GetTagLength(tag), (int)MAX_TEXT_EXTENT); - if(max_size == MAX_TEXT_EXTENT) - max_size--; - memcpy(format, (char*)FreeImage_GetTagValue(tag), max_size); - format[max_size] = '\0'; - buffer += format; - break; - } - } - - return buffer.c_str(); -} - -/** -Convert a Exif tag to a C string -*/ -static const char* -ConvertExifTag(FITAG *tag) { - char format[MAX_TEXT_EXTENT]; - static std::string buffer; - - if(!tag) - return NULL; - - buffer.erase(); - - // convert the tag value to a string buffer - - switch(FreeImage_GetTagID(tag)) { - case TAG_ORIENTATION: - { - unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (orientation) { - case 1: - return "top, left side"; - case 2: - return "top, right side"; - case 3: - return "bottom, right side"; - case 4: - return "bottom, left side"; - case 5: - return "left side, top"; - case 6: - return "right side, top"; - case 7: - return "right side, bottom"; - case 8: - return "left side, bottom"; - default: - break; - } - } - break; - - case TAG_REFERENCE_BLACK_WHITE: - { - DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); - if(FreeImage_GetTagLength(tag) == 48) { - // reference black point value and reference white point value (ReferenceBlackWhite) - int blackR = 0, whiteR = 0, blackG = 0, whiteG = 0, blackB = 0, whiteB = 0; - if(pvalue[1]) - blackR = (int)(pvalue[0] / pvalue[1]); - if(pvalue[3]) - whiteR = (int)(pvalue[2] / pvalue[3]); - if(pvalue[5]) - blackG = (int)(pvalue[4] / pvalue[5]); - if(pvalue[7]) - whiteG = (int)(pvalue[6] / pvalue[7]); - if(pvalue[9]) - blackB = (int)(pvalue[8] / pvalue[9]); - if(pvalue[11]) - whiteB = (int)(pvalue[10] / pvalue[11]); - - sprintf(format, "[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); - buffer += format; - return buffer.c_str(); - } - - } - break; - - case TAG_COLOR_SPACE: - { - unsigned short colorSpace = *((unsigned short *)FreeImage_GetTagValue(tag)); - if (colorSpace == 1) { - return "sRGB"; - } else if (colorSpace == 65535) { - return "Undefined"; - } else { - return "Unknown"; - } - } - break; - - case TAG_COMPONENTS_CONFIGURATION: - { - const char *componentStrings[7] = {"", "Y", "Cb", "Cr", "R", "G", "B"}; - BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag); - for(DWORD i = 0; i < MIN((DWORD)4, FreeImage_GetTagCount(tag)); i++) { - int j = pvalue[i]; - if(j > 0 && j < 7) - buffer += componentStrings[j]; - } - return buffer.c_str(); - } - break; - - case TAG_COMPRESSED_BITS_PER_PIXEL: - { - FIRational r(tag); - buffer = r.toString(); - if(buffer == "1") - buffer += " bit/pixel"; - else - buffer += " bits/pixel"; - return buffer.c_str(); - } - break; - - case TAG_X_RESOLUTION: - case TAG_Y_RESOLUTION: - case TAG_FOCAL_PLANE_X_RES: - case TAG_FOCAL_PLANE_Y_RES: - case TAG_BRIGHTNESS_VALUE: - case TAG_EXPOSURE_BIAS_VALUE: - { - FIRational r(tag); - buffer = r.toString(); - return buffer.c_str(); - } - break; - - case TAG_RESOLUTION_UNIT: - case TAG_FOCAL_PLANE_UNIT: - { - unsigned short resolutionUnit = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (resolutionUnit) { - case 1: - return "(No unit)"; - case 2: - return "inches"; - case 3: - return "cm"; - default: - break; - } - } - break; - - case TAG_YCBCR_POSITIONING: - { - unsigned short yCbCrPosition = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (yCbCrPosition) { - case 1: - return "Center of pixel array"; - case 2: - return "Datum point"; - default: - break; - } - } - break; - - case TAG_EXPOSURE_TIME: - { - FIRational r(tag); - buffer = r.toString(); - buffer += " sec"; - return buffer.c_str(); - } - break; - - case TAG_SHUTTER_SPEED_VALUE: - { - FIRational r(tag); - LONG apexValue = r.longValue(); - LONG apexPower = 1 << apexValue; - sprintf(format, "1/%d sec", (int)apexPower); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_APERTURE_VALUE: - case TAG_MAX_APERTURE_VALUE: - { - FIRational r(tag); - double apertureApex = r.doubleValue(); - double rootTwo = sqrt((double)2); - double fStop = pow(rootTwo, apertureApex); - sprintf(format, "F%.1f", fStop); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_FNUMBER: - { - FIRational r(tag); - double fnumber = r.doubleValue(); - sprintf(format, "F%.1f", fnumber); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_FOCAL_LENGTH: - { - FIRational r(tag); - double focalLength = r.doubleValue(); - sprintf(format, "%.1f mm", focalLength); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_FOCAL_LENGTH_IN_35MM_FILM: - { - unsigned short focalLength = *((unsigned short *)FreeImage_GetTagValue(tag)); - sprintf(format, "%hu mm", focalLength); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_FLASH: - { - unsigned short flash = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch(flash) { - case 0x0000: - return "Flash did not fire"; - case 0x0001: - return "Flash fired"; - case 0x0005: - return "Strobe return light not detected"; - case 0x0007: - return "Strobe return light detected"; - case 0x0009: - return "Flash fired, compulsory flash mode"; - case 0x000D: - return "Flash fired, compulsory flash mode, return light not detected"; - case 0x000F: - return "Flash fired, compulsory flash mode, return light detected"; - case 0x0010: - return "Flash did not fire, compulsory flash mode"; - case 0x0018: - return "Flash did not fire, auto mode"; - case 0x0019: - return "Flash fired, auto mode"; - case 0x001D: - return "Flash fired, auto mode, return light not detected"; - case 0x001F: - return "Flash fired, auto mode, return light detected"; - case 0x0020: - return "No flash function"; - case 0x0041: - return "Flash fired, red-eye reduction mode"; - case 0x0045: - return "Flash fired, red-eye reduction mode, return light not detected"; - case 0x0047: - return "Flash fired, red-eye reduction mode, return light detected"; - case 0x0049: - return "Flash fired, compulsory flash mode, red-eye reduction mode"; - case 0x004D: - return "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected"; - case 0x004F: - return "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected"; - case 0x0059: - return "Flash fired, auto mode, red-eye reduction mode"; - case 0x005D: - return "Flash fired, auto mode, return light not detected, red-eye reduction mode"; - case 0x005F: - return "Flash fired, auto mode, return light detected, red-eye reduction mode"; - default: - sprintf(format, "Unknown (%d)", flash); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SCENE_TYPE: - { - BYTE sceneType = *((BYTE*)FreeImage_GetTagValue(tag)); - if (sceneType == 1) { - return "Directly photographed image"; - } else { - sprintf(format, "Unknown (%d)", sceneType); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SUBJECT_DISTANCE: - { - FIRational r(tag); - if(r.getNumerator() == 0xFFFFFFFF) { - return "Infinity"; - } else if(r.getNumerator() == 0) { - return "Distance unknown"; - } else { - double distance = r.doubleValue(); - sprintf(format, "%.3f meters", distance); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_METERING_MODE: - { - unsigned short meteringMode = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (meteringMode) { - case 0: - return "Unknown"; - case 1: - return "Average"; - case 2: - return "Center weighted average"; - case 3: - return "Spot"; - case 4: - return "Multi-spot"; - case 5: - return "Multi-segment"; - case 6: - return "Partial"; - case 255: - return "(Other)"; - default: - return ""; - } - } - break; - - case TAG_LIGHT_SOURCE: - { - unsigned short lightSource = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (lightSource) { - case 0: - return "Unknown"; - case 1: - return "Daylight"; - case 2: - return "Fluorescent"; - case 3: - return "Tungsten (incandescent light)"; - case 4: - return "Flash"; - case 9: - return "Fine weather"; - case 10: - return "Cloudy weather"; - case 11: - return "Shade"; - case 12: - return "Daylight fluorescent (D 5700 - 7100K)"; - case 13: - return "Day white fluorescent (N 4600 - 5400K)"; - case 14: - return "Cool white fluorescent (W 3900 - 4500K)"; - case 15: - return "White fluorescent (WW 3200 - 3700K)"; - case 17: - return "Standard light A"; - case 18: - return "Standard light B"; - case 19: - return "Standard light C"; - case 20: - return "D55"; - case 21: - return "D65"; - case 22: - return "D75"; - case 23: - return "D50"; - case 24: - return "ISO studio tungsten"; - case 255: - return "(Other)"; - default: - return ""; - } - } - break; - - case TAG_SENSING_METHOD: - { - unsigned short sensingMethod = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (sensingMethod) { - case 1: - return "(Not defined)"; - case 2: - return "One-chip color area sensor"; - case 3: - return "Two-chip color area sensor"; - case 4: - return "Three-chip color area sensor"; - case 5: - return "Color sequential area sensor"; - case 7: - return "Trilinear sensor"; - case 8: - return "Color sequential linear sensor"; - default: - return ""; - } - } - break; - - case TAG_FILE_SOURCE: - { - BYTE fileSource = *((BYTE*)FreeImage_GetTagValue(tag)); - if (fileSource == 3) { - return "Digital Still Camera (DSC)"; - } else { - sprintf(format, "Unknown (%d)", fileSource); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_EXPOSURE_PROGRAM: - { - unsigned short exposureProgram = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (exposureProgram) { - case 1: - return "Manual control"; - case 2: - return "Program normal"; - case 3: - return "Aperture priority"; - case 4: - return "Shutter priority"; - case 5: - return "Program creative (slow program)"; - case 6: - return "Program action (high-speed program)"; - case 7: - return "Portrait mode"; - case 8: - return "Landscape mode"; - default: - sprintf(format, "Unknown program (%d)", exposureProgram); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_CUSTOM_RENDERED: - { - unsigned short customRendered = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (customRendered) { - case 0: - return "Normal process"; - case 1: - return "Custom process"; - default: - sprintf(format, "Unknown rendering (%d)", customRendered); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_EXPOSURE_MODE: - { - unsigned short exposureMode = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (exposureMode) { - case 0: - return "Auto exposure"; - case 1: - return "Manual exposure"; - case 2: - return "Auto bracket"; - default: - sprintf(format, "Unknown mode (%d)", exposureMode); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_WHITE_BALANCE: - { - unsigned short whiteBalance = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (whiteBalance) { - case 0: - return "Auto white balance"; - case 1: - return "Manual white balance"; - default: - sprintf(format, "Unknown (%d)", whiteBalance); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SCENE_CAPTURE_TYPE: - { - unsigned short sceneType = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (sceneType) { - case 0: - return "Standard"; - case 1: - return "Landscape"; - case 2: - return "Portrait"; - case 3: - return "Night scene"; - default: - sprintf(format, "Unknown (%d)", sceneType); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_GAIN_CONTROL: - { - unsigned short gainControl = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (gainControl) { - case 0: - return "None"; - case 1: - return "Low gain up"; - case 2: - return "High gain up"; - case 3: - return "Low gain down"; - case 4: - return "High gain down"; - default: - sprintf(format, "Unknown (%d)", gainControl); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_CONTRAST: - { - unsigned short contrast = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (contrast) { - case 0: - return "Normal"; - case 1: - return "Soft"; - case 2: - return "Hard"; - default: - sprintf(format, "Unknown (%d)", contrast); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SATURATION: - { - unsigned short saturation = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (saturation) { - case 0: - return "Normal"; - case 1: - return "Low saturation"; - case 2: - return "High saturation"; - default: - sprintf(format, "Unknown (%d)", saturation); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SHARPNESS: - { - unsigned short sharpness = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (sharpness) { - case 0: - return "Normal"; - case 1: - return "Soft"; - case 2: - return "Hard"; - default: - sprintf(format, "Unknown (%d)", sharpness); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_SUBJECT_DISTANCE_RANGE: - { - unsigned short distanceRange = *((unsigned short *)FreeImage_GetTagValue(tag)); - - switch (distanceRange) { - case 0: - return "unknown"; - case 1: - return "Macro"; - case 2: - return "Close view"; - case 3: - return "Distant view"; - default: - sprintf(format, "Unknown (%d)", distanceRange); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_ISO_SPEED_RATINGS: - { - unsigned short isoEquiv = *((unsigned short *)FreeImage_GetTagValue(tag)); - if (isoEquiv < 50) { - isoEquiv *= 200; - } - sprintf(format, "%d", isoEquiv); - buffer += format; - return buffer.c_str(); - } - break; - - case TAG_USER_COMMENT: - { - // first 8 bytes are used to define an ID code - // we assume this is an ASCII string - const BYTE *userComment = (BYTE*)FreeImage_GetTagValue(tag); - for(DWORD i = 8; i < FreeImage_GetTagLength(tag); i++) { - buffer += userComment[i]; - } - buffer += '\0'; - return buffer.c_str(); - } - break; - - case TAG_COMPRESSION: - { - WORD compression = *((WORD*)FreeImage_GetTagValue(tag)); - switch(compression) { - case TAG_COMPRESSION_NONE: - sprintf(format, "dump mode (%d)", compression); - break; - case TAG_COMPRESSION_CCITTRLE: - sprintf(format, "CCITT modified Huffman RLE (%d)", compression); - break; - case TAG_COMPRESSION_CCITTFAX3: - sprintf(format, "CCITT Group 3 fax encoding (%d)", compression); - break; - /* - case TAG_COMPRESSION_CCITT_T4: - sprintf(format, "CCITT T.4 (TIFF 6 name) (%d)", compression); - break; - */ - case TAG_COMPRESSION_CCITTFAX4: - sprintf(format, "CCITT Group 4 fax encoding (%d)", compression); - break; - /* - case TAG_COMPRESSION_CCITT_T6: - sprintf(format, "CCITT T.6 (TIFF 6 name) (%d)", compression); - break; - */ - case TAG_COMPRESSION_LZW: - sprintf(format, "LZW (%d)", compression); - break; - case TAG_COMPRESSION_OJPEG: - sprintf(format, "!6.0 JPEG (%d)", compression); - break; - case TAG_COMPRESSION_JPEG: - sprintf(format, "JPEG (%d)", compression); - break; - case TAG_COMPRESSION_NEXT: - sprintf(format, "NeXT 2-bit RLE (%d)", compression); - break; - case TAG_COMPRESSION_CCITTRLEW: - sprintf(format, "CCITTRLEW (%d)", compression); - break; - case TAG_COMPRESSION_PACKBITS: - sprintf(format, "PackBits Macintosh RLE (%d)", compression); - break; - case TAG_COMPRESSION_THUNDERSCAN: - sprintf(format, "ThunderScan RLE (%d)", compression); - break; - case TAG_COMPRESSION_PIXARFILM: - sprintf(format, "Pixar companded 10bit LZW (%d)", compression); - break; - case TAG_COMPRESSION_PIXARLOG: - sprintf(format, "Pixar companded 11bit ZIP (%d)", compression); - break; - case TAG_COMPRESSION_DEFLATE: - sprintf(format, "Deflate compression (%d)", compression); - break; - case TAG_COMPRESSION_ADOBE_DEFLATE: - sprintf(format, "Adobe Deflate compression (%d)", compression); - break; - case TAG_COMPRESSION_DCS: - sprintf(format, "Kodak DCS encoding (%d)", compression); - break; - case TAG_COMPRESSION_JBIG: - sprintf(format, "ISO JBIG (%d)", compression); - break; - case TAG_COMPRESSION_SGILOG: - sprintf(format, "SGI Log Luminance RLE (%d)", compression); - break; - case TAG_COMPRESSION_SGILOG24: - sprintf(format, "SGI Log 24-bit packed (%d)", compression); - break; - case TAG_COMPRESSION_JP2000: - sprintf(format, "Leadtools JPEG2000 (%d)", compression); - break; - case TAG_COMPRESSION_LZMA: - sprintf(format, "LZMA2 (%d)", compression); - break; - default: - sprintf(format, "Unknown type (%d)", compression); - break; - } - - buffer += format; - return buffer.c_str(); - } - break; - } - - return ConvertAnyTag(tag); -} - -/** -Convert a Exif GPS tag to a C string -*/ -static const char* -ConvertExifGPSTag(FITAG *tag) { - char format[MAX_TEXT_EXTENT]; - static std::string buffer; - - if(!tag) - return NULL; - - buffer.erase(); - - // convert the tag value to a string buffer - - switch(FreeImage_GetTagID(tag)) { - case TAG_GPS_LATITUDE: - case TAG_GPS_LONGITUDE: - case TAG_GPS_TIME_STAMP: - { - DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); - if(FreeImage_GetTagLength(tag) == 24) { - // dd:mm:ss or hh:mm:ss - int dd = 0, mm = 0; - double ss = 0; - - // convert to seconds - if(pvalue[1]) - ss += ((double)pvalue[0] / (double)pvalue[1]) * 3600; - if(pvalue[3]) - ss += ((double)pvalue[2] / (double)pvalue[3]) * 60; - if(pvalue[5]) - ss += ((double)pvalue[4] / (double)pvalue[5]); - - // convert to dd:mm:ss.ss - dd = (int)(ss / 3600); - mm = (int)(ss / 60) - dd * 60; - ss = ss - dd * 3600 - mm * 60; - - sprintf(format, "%d:%d:%.2f", dd, mm, ss); - buffer += format; - return buffer.c_str(); - } - } - break; - - case TAG_GPS_VERSION_ID: - case TAG_GPS_LATITUDE_REF: - case TAG_GPS_LONGITUDE_REF: - case TAG_GPS_ALTITUDE_REF: - case TAG_GPS_ALTITUDE: - case TAG_GPS_SATELLITES: - case TAG_GPS_STATUS: - case TAG_GPS_MEASURE_MODE: - case TAG_GPS_DOP: - case TAG_GPS_SPEED_REF: - case TAG_GPS_SPEED: - case TAG_GPS_TRACK_REF: - case TAG_GPS_TRACK: - case TAG_GPS_IMG_DIRECTION_REF: - case TAG_GPS_IMG_DIRECTION: - case TAG_GPS_MAP_DATUM: - case TAG_GPS_DEST_LATITUDE_REF: - case TAG_GPS_DEST_LATITUDE: - case TAG_GPS_DEST_LONGITUDE_REF: - case TAG_GPS_DEST_LONGITUDE: - case TAG_GPS_DEST_BEARING_REF: - case TAG_GPS_DEST_BEARING: - case TAG_GPS_DEST_DISTANCE_REF: - case TAG_GPS_DEST_DISTANCE: - case TAG_GPS_PROCESSING_METHOD: - case TAG_GPS_AREA_INFORMATION: - case TAG_GPS_DATE_STAMP: - case TAG_GPS_DIFFERENTIAL: - break; - } - - return ConvertAnyTag(tag); -} - -// ========================================================== -// Tag to string conversion function -// - -const char* DLL_CALLCONV -FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make) { - switch(model) { - case FIMD_EXIF_MAIN: - case FIMD_EXIF_EXIF: - return ConvertExifTag(tag); - - case FIMD_EXIF_GPS: - return ConvertExifGPSTag(tag); - - case FIMD_EXIF_MAKERNOTE: - // We should use the Make string to select an appropriate conversion function - // TO DO ... - break; - - case FIMD_EXIF_INTEROP: - default: - break; - } - - return ConvertAnyTag(tag); -} - +// ========================================================== +// Tag to string conversion functions +// +// Design and implementation by +// - Hervé Drolon +// +// 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 "FreeImage.h" +#include "Utilities.h" +#include "FreeImageTag.h" +#include "FIRational.h" + +#define MAX_TEXT_EXTENT 512 + +/** +Convert a tag to a C string +*/ +static const char* +ConvertAnyTag(FITAG *tag) { + char format[MAX_TEXT_EXTENT]; + static std::string buffer; + DWORD i; + + if(!tag) + return NULL; + + buffer.erase(); + + // convert the tag value to a string buffer + + FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); + DWORD tag_count = FreeImage_GetTagCount(tag); + + switch(tag_type) { + case FIDT_BYTE: // N x 8-bit unsigned integer + { + BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld", (LONG) pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %ld", (LONG) pvalue[i]); + buffer += format; + } + break; + } + case FIDT_SHORT: // N x 16-bit unsigned integer + { + unsigned short *pvalue = (unsigned short *)FreeImage_GetTagValue(tag); + + sprintf(format, "%hu", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %hu", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_LONG: // N x 32-bit unsigned integer + { + DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag); + + sprintf(format, "%lu", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %lu", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_RATIONAL: // N x 64-bit unsigned fraction + { + DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]); + buffer += format; + } + break; + } + case FIDT_SBYTE: // N x 8-bit signed integer + { + char *pvalue = (char*)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld", (LONG) pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %ld", (LONG) pvalue[i]); + buffer += format; + } + break; + } + case FIDT_SSHORT: // N x 16-bit signed integer + { + short *pvalue = (short *)FreeImage_GetTagValue(tag); + + sprintf(format, "%hd", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %hd", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_SLONG: // N x 32-bit signed integer + { + LONG *pvalue = (LONG *)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %ld", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_SRATIONAL:// N x 64-bit signed fraction + { + LONG *pvalue = (LONG*)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]); + buffer += format; + } + break; + } + case FIDT_FLOAT: // N x 32-bit IEEE floating point + { + float *pvalue = (float *)FreeImage_GetTagValue(tag); + + sprintf(format, "%f", (double) pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, "%f", (double) pvalue[i]); + buffer += format; + } + break; + } + case FIDT_DOUBLE: // N x 64-bit IEEE floating point + { + double *pvalue = (double *)FreeImage_GetTagValue(tag); + + sprintf(format, "%f", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, "%f", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_IFD: // N x 32-bit unsigned integer (offset) + { + DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag); + + sprintf(format, "%X", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " %X", pvalue[i]); + buffer += format; + } + break; + } + case FIDT_PALETTE: // N x 32-bit RGBQUAD + { + RGBQUAD *pvalue = (RGBQUAD *)FreeImage_GetTagValue(tag); + + sprintf(format, "(%d,%d,%d,%d)", pvalue[0].rgbRed, pvalue[0].rgbGreen, pvalue[0].rgbBlue, pvalue[0].rgbReserved); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, " (%d,%d,%d,%d)", pvalue[i].rgbRed, pvalue[i].rgbGreen, pvalue[i].rgbBlue, pvalue[i].rgbReserved); + buffer += format; + } + break; + } + + case FIDT_LONG8: // N x 64-bit unsigned integer + { + UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, "%ld", pvalue[i]); + buffer += format; + } + break; + } + + case FIDT_IFD8: // N x 64-bit unsigned integer (offset) + { + UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag); + + sprintf(format, "%X", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, "%X", pvalue[i]); + buffer += format; + } + break; + } + + case FIDT_SLONG8: // N x 64-bit signed integer + { + INT64 *pvalue = (INT64 *)FreeImage_GetTagValue(tag); + + sprintf(format, "%ld", pvalue[0]); + buffer += format; + for(i = 1; i < tag_count; i++) { + sprintf(format, "%ld", pvalue[i]); + buffer += format; + } + break; + } + + case FIDT_ASCII: // 8-bit bytes w/ last byte null + case FIDT_UNDEFINED:// 8-bit untyped data + default: + { + int max_size = MIN((int)FreeImage_GetTagLength(tag), (int)MAX_TEXT_EXTENT); + if(max_size == MAX_TEXT_EXTENT) + max_size--; + memcpy(format, (char*)FreeImage_GetTagValue(tag), max_size); + format[max_size] = '\0'; + buffer += format; + break; + } + } + + return buffer.c_str(); +} + +/** +Convert a Exif tag to a C string +*/ +static const char* +ConvertExifTag(FITAG *tag) { + char format[MAX_TEXT_EXTENT]; + static std::string buffer; + + if(!tag) + return NULL; + + buffer.erase(); + + // convert the tag value to a string buffer + + switch(FreeImage_GetTagID(tag)) { + case TAG_ORIENTATION: + { + unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (orientation) { + case 1: + return "top, left side"; + case 2: + return "top, right side"; + case 3: + return "bottom, right side"; + case 4: + return "bottom, left side"; + case 5: + return "left side, top"; + case 6: + return "right side, top"; + case 7: + return "right side, bottom"; + case 8: + return "left side, bottom"; + default: + break; + } + } + break; + + case TAG_REFERENCE_BLACK_WHITE: + { + DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); + if(FreeImage_GetTagLength(tag) == 48) { + // reference black point value and reference white point value (ReferenceBlackWhite) + int blackR = 0, whiteR = 0, blackG = 0, whiteG = 0, blackB = 0, whiteB = 0; + if(pvalue[1]) + blackR = (int)(pvalue[0] / pvalue[1]); + if(pvalue[3]) + whiteR = (int)(pvalue[2] / pvalue[3]); + if(pvalue[5]) + blackG = (int)(pvalue[4] / pvalue[5]); + if(pvalue[7]) + whiteG = (int)(pvalue[6] / pvalue[7]); + if(pvalue[9]) + blackB = (int)(pvalue[8] / pvalue[9]); + if(pvalue[11]) + whiteB = (int)(pvalue[10] / pvalue[11]); + + sprintf(format, "[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB); + buffer += format; + return buffer.c_str(); + } + + } + break; + + case TAG_COLOR_SPACE: + { + unsigned short colorSpace = *((unsigned short *)FreeImage_GetTagValue(tag)); + if (colorSpace == 1) { + return "sRGB"; + } else if (colorSpace == 65535) { + return "Undefined"; + } else { + return "Unknown"; + } + } + break; + + case TAG_COMPONENTS_CONFIGURATION: + { + const char *componentStrings[7] = {"", "Y", "Cb", "Cr", "R", "G", "B"}; + BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag); + for(DWORD i = 0; i < MIN((DWORD)4, FreeImage_GetTagCount(tag)); i++) { + int j = pvalue[i]; + if(j > 0 && j < 7) + buffer += componentStrings[j]; + } + return buffer.c_str(); + } + break; + + case TAG_COMPRESSED_BITS_PER_PIXEL: + { + FIRational r(tag); + buffer = r.toString(); + if(buffer == "1") + buffer += " bit/pixel"; + else + buffer += " bits/pixel"; + return buffer.c_str(); + } + break; + + case TAG_X_RESOLUTION: + case TAG_Y_RESOLUTION: + case TAG_FOCAL_PLANE_X_RES: + case TAG_FOCAL_PLANE_Y_RES: + case TAG_BRIGHTNESS_VALUE: + case TAG_EXPOSURE_BIAS_VALUE: + { + FIRational r(tag); + buffer = r.toString(); + return buffer.c_str(); + } + break; + + case TAG_RESOLUTION_UNIT: + case TAG_FOCAL_PLANE_UNIT: + { + unsigned short resolutionUnit = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (resolutionUnit) { + case 1: + return "(No unit)"; + case 2: + return "inches"; + case 3: + return "cm"; + default: + break; + } + } + break; + + case TAG_YCBCR_POSITIONING: + { + unsigned short yCbCrPosition = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (yCbCrPosition) { + case 1: + return "Center of pixel array"; + case 2: + return "Datum point"; + default: + break; + } + } + break; + + case TAG_EXPOSURE_TIME: + { + FIRational r(tag); + buffer = r.toString(); + buffer += " sec"; + return buffer.c_str(); + } + break; + + case TAG_SHUTTER_SPEED_VALUE: + { + FIRational r(tag); + LONG apexValue = r.longValue(); + LONG apexPower = 1 << apexValue; + sprintf(format, "1/%d sec", (int)apexPower); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_APERTURE_VALUE: + case TAG_MAX_APERTURE_VALUE: + { + FIRational r(tag); + double apertureApex = r.doubleValue(); + double rootTwo = sqrt((double)2); + double fStop = pow(rootTwo, apertureApex); + sprintf(format, "F%.1f", fStop); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_FNUMBER: + { + FIRational r(tag); + double fnumber = r.doubleValue(); + sprintf(format, "F%.1f", fnumber); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_FOCAL_LENGTH: + { + FIRational r(tag); + double focalLength = r.doubleValue(); + sprintf(format, "%.1f mm", focalLength); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_FOCAL_LENGTH_IN_35MM_FILM: + { + unsigned short focalLength = *((unsigned short *)FreeImage_GetTagValue(tag)); + sprintf(format, "%hu mm", focalLength); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_FLASH: + { + unsigned short flash = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch(flash) { + case 0x0000: + return "Flash did not fire"; + case 0x0001: + return "Flash fired"; + case 0x0005: + return "Strobe return light not detected"; + case 0x0007: + return "Strobe return light detected"; + case 0x0009: + return "Flash fired, compulsory flash mode"; + case 0x000D: + return "Flash fired, compulsory flash mode, return light not detected"; + case 0x000F: + return "Flash fired, compulsory flash mode, return light detected"; + case 0x0010: + return "Flash did not fire, compulsory flash mode"; + case 0x0018: + return "Flash did not fire, auto mode"; + case 0x0019: + return "Flash fired, auto mode"; + case 0x001D: + return "Flash fired, auto mode, return light not detected"; + case 0x001F: + return "Flash fired, auto mode, return light detected"; + case 0x0020: + return "No flash function"; + case 0x0041: + return "Flash fired, red-eye reduction mode"; + case 0x0045: + return "Flash fired, red-eye reduction mode, return light not detected"; + case 0x0047: + return "Flash fired, red-eye reduction mode, return light detected"; + case 0x0049: + return "Flash fired, compulsory flash mode, red-eye reduction mode"; + case 0x004D: + return "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected"; + case 0x004F: + return "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected"; + case 0x0059: + return "Flash fired, auto mode, red-eye reduction mode"; + case 0x005D: + return "Flash fired, auto mode, return light not detected, red-eye reduction mode"; + case 0x005F: + return "Flash fired, auto mode, return light detected, red-eye reduction mode"; + default: + sprintf(format, "Unknown (%d)", flash); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SCENE_TYPE: + { + BYTE sceneType = *((BYTE*)FreeImage_GetTagValue(tag)); + if (sceneType == 1) { + return "Directly photographed image"; + } else { + sprintf(format, "Unknown (%d)", sceneType); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SUBJECT_DISTANCE: + { + FIRational r(tag); + if(r.getNumerator() == 0xFFFFFFFF) { + return "Infinity"; + } else if(r.getNumerator() == 0) { + return "Distance unknown"; + } else { + double distance = r.doubleValue(); + sprintf(format, "%.3f meters", distance); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_METERING_MODE: + { + unsigned short meteringMode = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (meteringMode) { + case 0: + return "Unknown"; + case 1: + return "Average"; + case 2: + return "Center weighted average"; + case 3: + return "Spot"; + case 4: + return "Multi-spot"; + case 5: + return "Multi-segment"; + case 6: + return "Partial"; + case 255: + return "(Other)"; + default: + return ""; + } + } + break; + + case TAG_LIGHT_SOURCE: + { + unsigned short lightSource = *((unsigned short *)FreeImage_GetTagValue(tag)); + switch (lightSource) { + case 0: + return "Unknown"; + case 1: + return "Daylight"; + case 2: + return "Fluorescent"; + case 3: + return "Tungsten (incandescent light)"; + case 4: + return "Flash"; + case 9: + return "Fine weather"; + case 10: + return "Cloudy weather"; + case 11: + return "Shade"; + case 12: + return "Daylight fluorescent (D 5700 - 7100K)"; + case 13: + return "Day white fluorescent (N 4600 - 5400K)"; + case 14: + return "Cool white fluorescent (W 3900 - 4500K)"; + case 15: + return "White fluorescent (WW 3200 - 3700K)"; + case 17: + return "Standard light A"; + case 18: + return "Standard light B"; + case 19: + return "Standard light C"; + case 20: + return "D55"; + case 21: + return "D65"; + case 22: + return "D75"; + case 23: + return "D50"; + case 24: + return "ISO studio tungsten"; + case 255: + return "(Other)"; + default: + return ""; + } + } + break; + + case TAG_SENSING_METHOD: + { + unsigned short sensingMethod = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (sensingMethod) { + case 1: + return "(Not defined)"; + case 2: + return "One-chip color area sensor"; + case 3: + return "Two-chip color area sensor"; + case 4: + return "Three-chip color area sensor"; + case 5: + return "Color sequential area sensor"; + case 7: + return "Trilinear sensor"; + case 8: + return "Color sequential linear sensor"; + default: + return ""; + } + } + break; + + case TAG_FILE_SOURCE: + { + BYTE fileSource = *((BYTE*)FreeImage_GetTagValue(tag)); + if (fileSource == 3) { + return "Digital Still Camera (DSC)"; + } else { + sprintf(format, "Unknown (%d)", fileSource); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_EXPOSURE_PROGRAM: + { + unsigned short exposureProgram = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (exposureProgram) { + case 1: + return "Manual control"; + case 2: + return "Program normal"; + case 3: + return "Aperture priority"; + case 4: + return "Shutter priority"; + case 5: + return "Program creative (slow program)"; + case 6: + return "Program action (high-speed program)"; + case 7: + return "Portrait mode"; + case 8: + return "Landscape mode"; + default: + sprintf(format, "Unknown program (%d)", exposureProgram); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_CUSTOM_RENDERED: + { + unsigned short customRendered = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (customRendered) { + case 0: + return "Normal process"; + case 1: + return "Custom process"; + default: + sprintf(format, "Unknown rendering (%d)", customRendered); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_EXPOSURE_MODE: + { + unsigned short exposureMode = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (exposureMode) { + case 0: + return "Auto exposure"; + case 1: + return "Manual exposure"; + case 2: + return "Auto bracket"; + default: + sprintf(format, "Unknown mode (%d)", exposureMode); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_WHITE_BALANCE: + { + unsigned short whiteBalance = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (whiteBalance) { + case 0: + return "Auto white balance"; + case 1: + return "Manual white balance"; + default: + sprintf(format, "Unknown (%d)", whiteBalance); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SCENE_CAPTURE_TYPE: + { + unsigned short sceneType = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (sceneType) { + case 0: + return "Standard"; + case 1: + return "Landscape"; + case 2: + return "Portrait"; + case 3: + return "Night scene"; + default: + sprintf(format, "Unknown (%d)", sceneType); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_GAIN_CONTROL: + { + unsigned short gainControl = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (gainControl) { + case 0: + return "None"; + case 1: + return "Low gain up"; + case 2: + return "High gain up"; + case 3: + return "Low gain down"; + case 4: + return "High gain down"; + default: + sprintf(format, "Unknown (%d)", gainControl); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_CONTRAST: + { + unsigned short contrast = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (contrast) { + case 0: + return "Normal"; + case 1: + return "Soft"; + case 2: + return "Hard"; + default: + sprintf(format, "Unknown (%d)", contrast); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SATURATION: + { + unsigned short saturation = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (saturation) { + case 0: + return "Normal"; + case 1: + return "Low saturation"; + case 2: + return "High saturation"; + default: + sprintf(format, "Unknown (%d)", saturation); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SHARPNESS: + { + unsigned short sharpness = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (sharpness) { + case 0: + return "Normal"; + case 1: + return "Soft"; + case 2: + return "Hard"; + default: + sprintf(format, "Unknown (%d)", sharpness); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_SUBJECT_DISTANCE_RANGE: + { + unsigned short distanceRange = *((unsigned short *)FreeImage_GetTagValue(tag)); + + switch (distanceRange) { + case 0: + return "unknown"; + case 1: + return "Macro"; + case 2: + return "Close view"; + case 3: + return "Distant view"; + default: + sprintf(format, "Unknown (%d)", distanceRange); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_ISO_SPEED_RATINGS: + { + unsigned short isoEquiv = *((unsigned short *)FreeImage_GetTagValue(tag)); + if (isoEquiv < 50) { + isoEquiv *= 200; + } + sprintf(format, "%d", isoEquiv); + buffer += format; + return buffer.c_str(); + } + break; + + case TAG_USER_COMMENT: + { + // first 8 bytes are used to define an ID code + // we assume this is an ASCII string + const BYTE *userComment = (BYTE*)FreeImage_GetTagValue(tag); + for(DWORD i = 8; i < FreeImage_GetTagLength(tag); i++) { + buffer += userComment[i]; + } + buffer += '\0'; + return buffer.c_str(); + } + break; + + case TAG_COMPRESSION: + { + WORD compression = *((WORD*)FreeImage_GetTagValue(tag)); + switch(compression) { + case TAG_COMPRESSION_NONE: + sprintf(format, "dump mode (%d)", compression); + break; + case TAG_COMPRESSION_CCITTRLE: + sprintf(format, "CCITT modified Huffman RLE (%d)", compression); + break; + case TAG_COMPRESSION_CCITTFAX3: + sprintf(format, "CCITT Group 3 fax encoding (%d)", compression); + break; + /* + case TAG_COMPRESSION_CCITT_T4: + sprintf(format, "CCITT T.4 (TIFF 6 name) (%d)", compression); + break; + */ + case TAG_COMPRESSION_CCITTFAX4: + sprintf(format, "CCITT Group 4 fax encoding (%d)", compression); + break; + /* + case TAG_COMPRESSION_CCITT_T6: + sprintf(format, "CCITT T.6 (TIFF 6 name) (%d)", compression); + break; + */ + case TAG_COMPRESSION_LZW: + sprintf(format, "LZW (%d)", compression); + break; + case TAG_COMPRESSION_OJPEG: + sprintf(format, "!6.0 JPEG (%d)", compression); + break; + case TAG_COMPRESSION_JPEG: + sprintf(format, "JPEG (%d)", compression); + break; + case TAG_COMPRESSION_NEXT: + sprintf(format, "NeXT 2-bit RLE (%d)", compression); + break; + case TAG_COMPRESSION_CCITTRLEW: + sprintf(format, "CCITTRLEW (%d)", compression); + break; + case TAG_COMPRESSION_PACKBITS: + sprintf(format, "PackBits Macintosh RLE (%d)", compression); + break; + case TAG_COMPRESSION_THUNDERSCAN: + sprintf(format, "ThunderScan RLE (%d)", compression); + break; + case TAG_COMPRESSION_PIXARFILM: + sprintf(format, "Pixar companded 10bit LZW (%d)", compression); + break; + case TAG_COMPRESSION_PIXARLOG: + sprintf(format, "Pixar companded 11bit ZIP (%d)", compression); + break; + case TAG_COMPRESSION_DEFLATE: + sprintf(format, "Deflate compression (%d)", compression); + break; + case TAG_COMPRESSION_ADOBE_DEFLATE: + sprintf(format, "Adobe Deflate compression (%d)", compression); + break; + case TAG_COMPRESSION_DCS: + sprintf(format, "Kodak DCS encoding (%d)", compression); + break; + case TAG_COMPRESSION_JBIG: + sprintf(format, "ISO JBIG (%d)", compression); + break; + case TAG_COMPRESSION_SGILOG: + sprintf(format, "SGI Log Luminance RLE (%d)", compression); + break; + case TAG_COMPRESSION_SGILOG24: + sprintf(format, "SGI Log 24-bit packed (%d)", compression); + break; + case TAG_COMPRESSION_JP2000: + sprintf(format, "Leadtools JPEG2000 (%d)", compression); + break; + case TAG_COMPRESSION_LZMA: + sprintf(format, "LZMA2 (%d)", compression); + break; + default: + sprintf(format, "Unknown type (%d)", compression); + break; + } + + buffer += format; + return buffer.c_str(); + } + break; + } + + return ConvertAnyTag(tag); +} + +/** +Convert a Exif GPS tag to a C string +*/ +static const char* +ConvertExifGPSTag(FITAG *tag) { + char format[MAX_TEXT_EXTENT]; + static std::string buffer; + + if(!tag) + return NULL; + + buffer.erase(); + + // convert the tag value to a string buffer + + switch(FreeImage_GetTagID(tag)) { + case TAG_GPS_LATITUDE: + case TAG_GPS_LONGITUDE: + case TAG_GPS_TIME_STAMP: + { + DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag); + if(FreeImage_GetTagLength(tag) == 24) { + // dd:mm:ss or hh:mm:ss + int dd = 0, mm = 0; + double ss = 0; + + // convert to seconds + if(pvalue[1]) + ss += ((double)pvalue[0] / (double)pvalue[1]) * 3600; + if(pvalue[3]) + ss += ((double)pvalue[2] / (double)pvalue[3]) * 60; + if(pvalue[5]) + ss += ((double)pvalue[4] / (double)pvalue[5]); + + // convert to dd:mm:ss.ss + dd = (int)(ss / 3600); + mm = (int)(ss / 60) - dd * 60; + ss = ss - dd * 3600 - mm * 60; + + sprintf(format, "%d:%d:%.2f", dd, mm, ss); + buffer += format; + return buffer.c_str(); + } + } + break; + + case TAG_GPS_VERSION_ID: + case TAG_GPS_LATITUDE_REF: + case TAG_GPS_LONGITUDE_REF: + case TAG_GPS_ALTITUDE_REF: + case TAG_GPS_ALTITUDE: + case TAG_GPS_SATELLITES: + case TAG_GPS_STATUS: + case TAG_GPS_MEASURE_MODE: + case TAG_GPS_DOP: + case TAG_GPS_SPEED_REF: + case TAG_GPS_SPEED: + case TAG_GPS_TRACK_REF: + case TAG_GPS_TRACK: + case TAG_GPS_IMG_DIRECTION_REF: + case TAG_GPS_IMG_DIRECTION: + case TAG_GPS_MAP_DATUM: + case TAG_GPS_DEST_LATITUDE_REF: + case TAG_GPS_DEST_LATITUDE: + case TAG_GPS_DEST_LONGITUDE_REF: + case TAG_GPS_DEST_LONGITUDE: + case TAG_GPS_DEST_BEARING_REF: + case TAG_GPS_DEST_BEARING: + case TAG_GPS_DEST_DISTANCE_REF: + case TAG_GPS_DEST_DISTANCE: + case TAG_GPS_PROCESSING_METHOD: + case TAG_GPS_AREA_INFORMATION: + case TAG_GPS_DATE_STAMP: + case TAG_GPS_DIFFERENTIAL: + break; + } + + return ConvertAnyTag(tag); +} + +// ========================================================== +// Tag to string conversion function +// + +const char* DLL_CALLCONV +FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make) { + switch(model) { + case FIMD_EXIF_MAIN: + case FIMD_EXIF_EXIF: + return ConvertExifTag(tag); + + case FIMD_EXIF_GPS: + return ConvertExifGPSTag(tag); + + case FIMD_EXIF_MAKERNOTE: + // We should use the Make string to select an appropriate conversion function + // TO DO ... + break; + + case FIMD_EXIF_INTEROP: + default: + break; + } + + return ConvertAnyTag(tag); +} + diff --git a/plugins/AdvaImg/src/Metadata/XTIFF.cpp b/plugins/AdvaImg/src/Metadata/XTIFF.cpp index e6f6bdd0d9..681eed12c5 100644 --- a/plugins/AdvaImg/src/Metadata/XTIFF.cpp +++ b/plugins/AdvaImg/src/Metadata/XTIFF.cpp @@ -1,665 +1,675 @@ -// ========================================================== -// 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 "../LibTIFF4/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, "GeoPixelScale" }, - { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "Intergraph TransformationMatrix" }, - { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTransformationMatrix" }, - { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" }, - { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" }, - { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" }, - { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoASCIIParams" }, - { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, "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 -*/ -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 -// ---------------------------------------------------------- - -void -tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { - char defaultKey[16]; - - size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); - - TagLib& tag_lib = TagLib::instance(); - - for(unsigned 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; - - 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; - - 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) -} - -void -tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { - char defaultKey[16]; - - if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) { - return; - } - - size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); - - TagLib& tag_lib = TagLib::instance(); - - for(unsigned 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)); - } - } - } -} - -// ---------------------------------------------------------- -// EXIF tag reading & writing -// ---------------------------------------------------------- - -/** -Read a single exif tag -*/ -static BOOL -tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& tagLib, TIFFDirectory *td, uint32 tag) { - const TIFFField *fip; - uint32 value_count; - int mem_alloc = 0; - void *raw_data = NULL; - - if(tag == TIFFTAG_EXIFIFD) { - return TRUE; - } - - // get the tag key - use NULL to avoid reading GeoTIFF tags - const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL); - if(key == NULL) { - return TRUE; - } - - fip = TIFFFieldWithTag(tif, tag); - if(fip == NULL) { - return TRUE; - } - - if(fip->field_passcount) { //<- "passcount" means "returns count" - if (fip->field_readcount != TIFF_VARIABLE2) { //<- TIFF_VARIABLE2 means "uses LONG count" - - // assume TIFF_VARIABLE (uses SHORT count) - uint16 value_count16; - if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) { - return TRUE; - } - value_count = value_count16; - } else { - if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) { - return TRUE; - } - } - } else { - - // determine count - - if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) { - value_count = 1; - } else if (fip->field_readcount == TIFF_SPP) { - value_count = td->td_samplesperpixel; - } else { - value_count = fip->field_readcount; - } - - // access fields as pointers to data - // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) - - if(fip->field_tag == TIFFTAG_TRANSFERFUNCTION) { - // reading this tag cause a bug probably located somewhere inside libtiff - return TRUE; - } - - if ((fip->field_type == TIFF_ASCII - || fip->field_readcount == TIFF_VARIABLE - || fip->field_readcount == TIFF_VARIABLE2 - || fip->field_readcount == TIFF_SPP - || value_count > 1) - - && fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE - - && fip->field_tag != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - - && fip->field_tag != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value - ) { - if(TIFFGetField(tif, tag, &raw_data) != 1) { - return TRUE; - } - } else { - - // access fields as values - - const int value_size = _TIFFDataSize(fip->field_type); - 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, raw_data); - break; - case 2: - ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1); - break; -/* # we might need more in the future: - case 3: - ok = TIFFGetField(tif, tag, 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", fip->field_name); - 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); - FreeImage_SetTagKey(fitag, key); - - switch(fip->field_type) { - case TIFF_BYTE: - FreeImage_SetTagType(fitag, FIDT_BYTE); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * value_count); - FreeImage_SetTagCount(fitag, value_count); - FreeImage_SetTagValue(fitag, raw_data); - break; - - default: { - // remember that raw_data = _TIFFmalloc(value_size * value_count); - const int value_size = _TIFFDataSize(fip->field_type); - size_t 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); - 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 -*/ -BOOL -tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { - int i; - short count; - - TagLib& tagLib = TagLib::instance(); - - TIFFDirectory *td = &tif->tif_dir; - - count = (short) TIFFGetTagListCount(tif); - for(i = 0; i < count; i++) { - uint32 tag = TIFFGetTagListEntry(tif, i); - // read the tag - if (!tiff_read_exif_tag(tif, md_model, dib, tagLib, td, tag)) - 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) { - - 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]; - - if(fld->field_tag == lastTag) - continue; - - // test if tag value is set - // (lifted directly form LibTiff _TIFFWriteDirectory) - - if( fld->field_bit == FIELD_CUSTOM ) { - int ci, is_set = FALSE; - - for( 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, md_model, dib, tagLib, td, fld->field_tag); - - - lastTag = fld->field_tag; - } - - } - - 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_SAMPLEFORMAT: - case TIFFTAG_IMAGEWIDTH: - case TIFFTAG_IMAGELENGTH: - case TIFFTAG_SAMPLESPERPIXEL: - case TIFFTAG_BITSPERSAMPLE: - case TIFFTAG_PHOTOMETRIC: - case TIFFTAG_PLANARCONFIG: - case TIFFTAG_ROWSPERSTRIP: - case TIFFTAG_STRIPBYTECOUNTS: - case TIFFTAG_STRIPOFFSETS: - case TIFFTAG_RESOLUTIONUNIT: - case TIFFTAG_XRESOLUTION: - case TIFFTAG_YRESOLUTION: - case TIFFTAG_SUBFILETYPE: - case TIFFTAG_PAGENUMBER: - case TIFFTAG_COLORMAP: - case TIFFTAG_ORIENTATION: - case TIFFTAG_COMPRESSION: - case TIFFTAG_PREDICTOR: - case TIFFTAG_GROUP3OPTIONS: - case TIFFTAG_FILLORDER: - // 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 -*/ -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]; - - if(skip_write_field(tif, fld->field_tag)) { - // 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)fld->field_tag, defaultKey); - - if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { - FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); - TIFFDataType tif_tag_type = fld->field_type; - - // 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(_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { - // skip tag or _TIFFmemcpy will fail - continue; - } - - if(tag_type == FIDT_ASCII) { - TIFFSetField(tif, fld->field_tag, FreeImage_GetTagValue(tag)); - } else { - TIFFSetField(tif, fld->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); - } - } - } - - return TRUE; -} +// ========================================================== +// 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 "../LibTIFF4/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, "GeoPixelScale" }, + { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "Intergraph TransformationMatrix" }, + { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTransformationMatrix" }, + { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" }, + { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" }, + { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" }, + { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoASCIIParams" }, + { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, "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 +*/ +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 +// ---------------------------------------------------------- + +void +tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { + char defaultKey[16]; + + size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); + + TagLib& tag_lib = TagLib::instance(); + + for(unsigned 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; + + 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; + + 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) +} + +void +tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { + char defaultKey[16]; + + if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) { + return; + } + + size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); + + TagLib& tag_lib = TagLib::instance(); + + for(unsigned 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)); + } + } + } +} + +// ---------------------------------------------------------- +// EXIF tag reading & writing +// ---------------------------------------------------------- + +/** +Read a single exif tag +*/ +static BOOL +tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& tagLib, TIFFDirectory *td, uint32 tag) { + const TIFFField *fip; + uint32 value_count; + int mem_alloc = 0; + void *raw_data = NULL; + + if(tag == TIFFTAG_EXIFIFD) { + return TRUE; + } + + // get the tag key - use NULL to avoid reading GeoTIFF tags + const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL); + if(key == NULL) { + return TRUE; + } + + fip = TIFFFieldWithTag(tif, tag); + if(fip == NULL) { + return TRUE; + } + + if(fip->field_passcount) { //<- "passcount" means "returns count" + if (fip->field_readcount != TIFF_VARIABLE2) { //<- TIFF_VARIABLE2 means "uses LONG count" + + // assume TIFF_VARIABLE (uses SHORT count) + uint16 value_count16; + if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) { + return TRUE; + } + value_count = value_count16; + } else { + if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) { + return TRUE; + } + } + } else { + + // determine count + + if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) { + value_count = 1; + } else if (fip->field_readcount == TIFF_SPP) { + value_count = td->td_samplesperpixel; + } else { + value_count = fip->field_readcount; + } + + // access fields as pointers to data + // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) + + if(fip->field_tag == TIFFTAG_TRANSFERFUNCTION) { + // reading this tag cause a bug probably located somewhere inside libtiff + return TRUE; + } + + if ((fip->field_type == TIFF_ASCII + || fip->field_readcount == TIFF_VARIABLE + || fip->field_readcount == TIFF_VARIABLE2 + || fip->field_readcount == TIFF_SPP + || value_count > 1) + + && fip->field_tag != TIFFTAG_PAGENUMBER + && fip->field_tag != TIFFTAG_HALFTONEHINTS + && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING + && fip->field_tag != TIFFTAG_DOTRANGE + + && fip->field_tag != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - + && fip->field_tag != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value + ) { + if(TIFFGetField(tif, tag, &raw_data) != 1) { + return TRUE; + } + } else { + + // access fields as values + + const int value_size = _TIFFDataSize(fip->field_type); + 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, raw_data); + break; + case 2: + ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1); + break; +/* # we might need more in the future: + case 3: + ok = TIFFGetField(tif, tag, 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", fip->field_name); + 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); + FreeImage_SetTagKey(fitag, key); + + switch(fip->field_type) { + case TIFF_BYTE: + FreeImage_SetTagType(fitag, FIDT_BYTE); + FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * 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(fip->field_type) * value_count); + FreeImage_SetTagCount(fitag, value_count); + FreeImage_SetTagValue(fitag, raw_data); + break; + + case TIFF_ASCII: + default: { + size_t length = 0; + if(!mem_alloc && (fip->field_type == TIFF_ASCII) && (fip->field_readcount == 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 = _TIFFDataSize(fip->field_type); + 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); + 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 +*/ +BOOL +tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { + int i; + short count; + + TagLib& tagLib = TagLib::instance(); + + TIFFDirectory *td = &tif->tif_dir; + + count = (short) TIFFGetTagListCount(tif); + for(i = 0; i < count; i++) { + uint32 tag = TIFFGetTagListEntry(tif, i); + // read the tag + if (!tiff_read_exif_tag(tif, md_model, dib, tagLib, td, tag)) + 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) { + + 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]; + + if(fld->field_tag == lastTag) + continue; + + // test if tag value is set + // (lifted directly form LibTiff _TIFFWriteDirectory) + + if( fld->field_bit == FIELD_CUSTOM ) { + int ci, is_set = FALSE; + + for( 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, md_model, dib, tagLib, td, fld->field_tag); + + + lastTag = fld->field_tag; + } + + } + + 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_SAMPLEFORMAT: + case TIFFTAG_IMAGEWIDTH: + case TIFFTAG_IMAGELENGTH: + case TIFFTAG_SAMPLESPERPIXEL: + case TIFFTAG_BITSPERSAMPLE: + case TIFFTAG_PHOTOMETRIC: + case TIFFTAG_PLANARCONFIG: + case TIFFTAG_ROWSPERSTRIP: + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_RESOLUTIONUNIT: + case TIFFTAG_XRESOLUTION: + case TIFFTAG_YRESOLUTION: + case TIFFTAG_SUBFILETYPE: + case TIFFTAG_PAGENUMBER: + case TIFFTAG_COLORMAP: + case TIFFTAG_ORIENTATION: + case TIFFTAG_COMPRESSION: + case TIFFTAG_PREDICTOR: + case TIFFTAG_GROUP3OPTIONS: + case TIFFTAG_FILLORDER: + // 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 +*/ +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]; + + if(skip_write_field(tif, fld->field_tag)) { + // 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)fld->field_tag, defaultKey); + + if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { + FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); + TIFFDataType tif_tag_type = fld->field_type; + + // 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(_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { + // skip tag or _TIFFmemcpy will fail + continue; + } + + if(tag_type == FIDT_ASCII) { + TIFFSetField(tif, fld->field_tag, FreeImage_GetTagValue(tag)); + } else { + TIFFSetField(tif, fld->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); + } + } + } + + return TRUE; +} -- cgit v1.2.3