summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/Metadata
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2015-05-18 07:31:16 +0000
committerKirill Volinsky <mataes2007@gmail.com>2015-05-18 07:31:16 +0000
commit76b86227951fdb5096572c36a256f07aee76def3 (patch)
tree713dcf30179d88b685605bdc8a03a5068832e4ff /plugins/AdvaImg/src/Metadata
parent4b289716d4cdd6b3ea29aec8d50e0b69afdc8384 (diff)
git-svn-id: http://svn.miranda-ng.org/main/trunk@13671 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AdvaImg/src/Metadata')
-rw-r--r--plugins/AdvaImg/src/Metadata/Exif.cpp478
-rw-r--r--plugins/AdvaImg/src/Metadata/FreeImageTag.cpp33
-rw-r--r--plugins/AdvaImg/src/Metadata/FreeImageTag.h25
-rw-r--r--plugins/AdvaImg/src/Metadata/XTIFF.cpp313
4 files changed, 619 insertions, 230 deletions
diff --git a/plugins/AdvaImg/src/Metadata/Exif.cpp b/plugins/AdvaImg/src/Metadata/Exif.cpp
index abc840607b..35265cb2ab 100644
--- a/plugins/AdvaImg/src/Metadata/Exif.cpp
+++ b/plugins/AdvaImg/src/Metadata/Exif.cpp
@@ -120,15 +120,15 @@ ReadInt32(BOOL msb_order, const void *buffer) {
return value;
}
-static unsigned short
+static WORD
ReadUint16(BOOL msb_order, const void *buffer) {
- unsigned short value;
+ WORD value;
if(msb_order) {
- value = (unsigned short) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]);
+ value = (WORD) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]);
return value;
}
- value = (unsigned short) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]);
+ value = (WORD) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]);
return value;
}
@@ -146,9 +146,9 @@ Process a IFD offset
Returns the offset and the metadata model for this tag
*/
static void
-processIFDOffset(FITAG *tag, char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
+processIFDOffset(FITAG *tag, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
// get the IFD offset
- *subdirOffset = (DWORD) ReadUint32(msb_order, pval);
+ *subdirOffset = ReadUint32(msb_order, pval);
// select a tag info table
switch(FreeImage_GetTagID(tag)) {
@@ -162,7 +162,6 @@ processIFDOffset(FITAG *tag, char *pval, BOOL msb_order, DWORD *subdirOffset, Ta
*md_model = TagLib::EXIF_INTEROP;
break;
}
-
}
/**
@@ -170,7 +169,7 @@ Process a maker note IFD offset
Returns the offset and the metadata model for this tag
*/
static void
-processMakerNote(FIBITMAP *dib, char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
+processMakerNote(FIBITMAP *dib, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
FITAG *tagMake = NULL;
*subdirOffset = 0;
@@ -238,12 +237,12 @@ processMakerNote(FIBITMAP *dib, char *pval, BOOL msb_order, DWORD *subdirOffset,
}
} else if ((memcmp("FUJIFILM", pval, 8) == 0) || (Maker && (FreeImage_strnicmp("Fujifilm", Maker, 8) == 0))) {
// Fujifile Makernote
- // Fujifilm's Makernote always use Intel order altough the Exif section maybe in Intel order or in Motorola order.
+ // Fujifilm's Makernote always use little-endian order altough the Exif section maybe in little-endian order or in big-endian order.
// If msb_order == TRUE, the Makernote won't be read:
// the value of ifdStart will be 0x0c000000 instead of 0x0000000c and the MakerNote section will be discarded later
// in jpeg_read_exif_dir because the IFD is too high
*md_model = TagLib::EXIF_MAKERNOTE_FUJIFILM;
- DWORD ifdStart = (DWORD) ReadUint32(msb_order, pval + 8);
+ DWORD ifdStart = ReadUint32(msb_order, pval + 8);
*subdirOffset = ifdStart;
}
else if(memcmp("KYOCERA\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00", pval, 22) == 0) {
@@ -519,14 +518,15 @@ 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 dwOffsetIfd0 Offset to the 0th IFD (first IFD)
+@param dwLength Length of the Exif file
+@param dwProfileOffset File offset to be used when reading 'offset/value' tags
+@param msb_order Endianness order of the Exif file (TRUE if big-endian, FALSE if little-endian)
@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, TagLib::MDMODEL starting_md_model) {
+jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, DWORD dwOffsetIfd0, DWORD dwLength, DWORD dwProfileOffset, BOOL msb_order, TagLib::MDMODEL starting_md_model) {
WORD de, nde;
std::stack<WORD> destack; // directory entries stack
@@ -553,7 +553,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// set the pointer to the first IFD (0th IFD) and follow it were it leads.
- const BYTE *ifd0th = (BYTE*)tiffp + offset;
+ const BYTE *ifd0th = (BYTE*)tiffp + (size_t)dwOffsetIfd0;
const BYTE *ifdp = ifd0th;
@@ -577,6 +577,10 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// determine how many entries there are in the current IFD
nde = ReadUint16(msb_order, ifdp);
+ if (((size_t)(ifdp - tiffp) + 12 * nde) > (size_t)dwLength) {
+ // suspicious IFD offset, ignore
+ continue;
+ }
for(; de < nde; de++) {
char *pde = NULL; // pointer to the directory entry
@@ -590,7 +594,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
pde = (char*) DIR_ENTRY_ADDR(ifdp, de);
// get the tag ID
- FreeImage_SetTagID(tag, ReadUint16(msb_order, pde));
+ WORD tag_id = ReadUint16(msb_order, pde);
+ FreeImage_SetTagID(tag, tag_id);
+
// get the tag type
WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
if((tag_type - 1) >= EXIF_NUM_FORMATS) {
@@ -602,7 +608,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);
// get number of components
- FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4));
+ DWORD tag_count = ReadUint32(msb_order, pde + 4);
+ FreeImage_SetTagCount(tag, tag_count);
+
// check that tag length (size of the tag value in bytes) will fit in a DWORD
unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag));
if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) {
@@ -616,17 +624,21 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// 4 bytes or less and value is in the dir entry itself
pval = pde + 8;
} else {
- // if its bigger than 4 bytes, the directory entry contains an offset
- // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
+ // if its bigger than 4 bytes, the directory entry contains an offset
DWORD offset_value = ReadUint32(msb_order, pde + 8);
- if(offset_value > length) {
+ // the offset can be relative to tiffp or to an external reference (see JPEG-XR)
+ if(dwProfileOffset) {
+ offset_value -= dwProfileOffset;
+ }
+ // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
+ if(offset_value > dwLength) {
// a problem occured : delete the tag (not free'd after)
FreeImage_DeleteTag(tag);
// jump to next entry
continue;
}
// now check that length does not exceed the buffer size
- if(FreeImage_GetTagLength(tag) > length - offset_value){
+ if(FreeImage_GetTagLength(tag) > dwLength - offset_value){
// a problem occured : delete the tag (not free'd after)
FreeImage_DeleteTag(tag);
// jump to next entry
@@ -659,10 +671,10 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
next_ifd = (BYTE*)tiffp + sub_offset;
}
- if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) {
+ if((sub_offset < dwLength) && (next_mdmodel != TagLib::UNKNOWN)) {
// push our current directory state onto the stack
ifdstack.push(ifdp);
- // bump to the next entry
+ // jump to the next entry
de++;
destack.push(de);
@@ -708,7 +720,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
const WORD entriesCount0th = ReadUint16(msb_order, ifd0th);
DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th));
- if((next_offset == 0) || (next_offset >= length)) {
+ if((next_offset == 0) || (next_offset >= dwLength)) {
return TRUE; //< no thumbnail
}
@@ -726,7 +738,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// check for buffer overflow
const size_t remaining = (size_t)base + 12 - (size_t)tiffp;
- if(remaining >= length) {
+ if(remaining >= dwLength) {
// bad IFD1 directory, ignore it
return FALSE;
}
@@ -734,9 +746,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// get the tag ID
WORD tag = ReadUint16(msb_order, base);
// get the tag type
- WORD type = ReadUint16(msb_order, base + sizeof(WORD));
+ /*WORD type = */ReadUint16(msb_order, base + sizeof(WORD));
// get number of components
- DWORD count = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
+ /*DWORD count = */ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
// get the tag value
DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD));
@@ -768,7 +780,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
return TRUE;
}
- if(thOffset + thSize > length) {
+ if(thOffset + thSize > dwLength) {
return TRUE;
}
@@ -791,43 +803,45 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig
// --------------------------------------------------------------------------
/**
- Read and decode 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
+Read and decode JPEG_APP1 marker (Exif profile)
+@param dib Input FIBITMAP
+@param data Pointer to the APP1 marker
+@param length APP1 marker length
+@return Returns TRUE if successful, FALSE otherwise
*/
BOOL
-jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) {
+jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *data, unsigned length) {
// 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
- BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A }; // Motorola order
+ BYTE lsb_first[4] = { 0x49, 0x49, 0x2A, 0x00 }; // Classic TIFF signature - little-endian order
+ BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A }; // Classic TIFF signature - big-endian order
- unsigned int length = datalen;
- BYTE *profile = (BYTE*)dataptr;
+ // profile size is up to 32-bit
+ DWORD dwProfileLength = (DWORD)length;
+ BYTE *pbProfile = (BYTE*)data;
// verify the identifying string
+ if(memcmp(exif_signature, pbProfile, sizeof(exif_signature)) == 0) {
+ // This is an Exif profile
+ // should contain a TIFF header with up to 2 IFDs (IFD stands for 'Image File Directory')
+ // 0th IFD : the image attributes, 1st IFD : may be used for thumbnail
- if(memcmp(exif_signature, profile, sizeof(exif_signature)) == 0) {
- // Exif profile - TIFF header with 2 IFDs. 0th - the image attributes, 1st - may be used for thumbnail
-
- profile += sizeof(exif_signature);
- length -= sizeof(exif_signature);
+ pbProfile += sizeof(exif_signature);
+ dwProfileLength -= sizeof(exif_signature);
// read the TIFF header (8 bytes)
// check the endianess order
- BOOL bMotorolaOrder = TRUE;
+ BOOL bBigEndian = TRUE;
- if(memcmp(profile, lsb_first, sizeof(lsb_first)) == 0) {
- // Exif section in Intel order
- bMotorolaOrder = FALSE;
+ if(memcmp(pbProfile, lsb_first, sizeof(lsb_first)) == 0) {
+ // Exif section is in little-endian order
+ bBigEndian = FALSE;
} else {
- if(memcmp(profile, msb_first, sizeof(msb_first)) == 0) {
- // Exif section in Motorola order
- bMotorolaOrder = TRUE;
+ if(memcmp(pbProfile, msb_first, sizeof(msb_first)) == 0) {
+ // Exif section is in big-endian order
+ bBigEndian = TRUE;
} else {
// Invalid Exif alignment marker
return FALSE;
@@ -835,8 +849,8 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) {
}
// this is the offset to the first IFD (Image File Directory)
- unsigned long first_offset = ReadUint32(bMotorolaOrder, profile + 4);
- if (first_offset > length) {
+ DWORD dwFirstOffset = ReadUint32(bBigEndian, pbProfile + 4);
+ if (dwFirstOffset > dwProfileLength) {
// bad Exif data
return FALSE;
}
@@ -844,7 +858,7 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) {
/*
Note: as FreeImage 3.14.0, this test is no longer needed for images with similar suspicious offset
=> tested with Pentax Optio 230, FujiFilm SP-2500 and Canon EOS 300D
- if (first_offset < 8 || first_offset > 16) {
+ if (dwFirstOffset < 8 || dwFirstOffset > 16) {
// This is usually set to 8
// but PENTAX Optio 230 has it set differently, and uses it as offset.
FreeImage_OutputMessageProc(FIF_JPEG, "Exif: Suspicious offset of first IFD value");
@@ -853,18 +867,22 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) {
*/
// process Exif directories, starting with Exif-TIFF IFD
- return jpeg_read_exif_dir(dib, profile, first_offset, length, bMotorolaOrder, TagLib::EXIF_MAIN);
+ return jpeg_read_exif_dir(dib, pbProfile, dwFirstOffset, dwProfileLength, 0, bBigEndian, TagLib::EXIF_MAIN);
}
return FALSE;
}
+// ==========================================================
+// Exif JPEG helper routines
+// ==========================================================
+
/**
- 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
+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) {
@@ -898,38 +916,48 @@ jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length)
return FALSE;
}
+// ==========================================================
+// Exif JPEG-XR helper routines
+// ==========================================================
+
/**
Read and decode JPEG-XR Exif IFD
@param dib Input FIBITMAP
@param profile Pointer to the Exif marker
@param length Exif marker length
+@param file_offset Reference offset in the original file of each tag value whose length is > 4
@return Returns TRUE if successful, FALSE otherwise
*/
BOOL
-jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) {
+jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) {
// assume Little Endian order
- BOOL bMotorolaOrder = FALSE;
+ BOOL bBigEndian = FALSE;
// process Exif specific IFD
- return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_EXIF);
+ return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, 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
+@param profile Pointer to the Exif-GPS profile
+@param length Exif-GPS profile length
+@param file_offset Reference offset in the original file of each tag value whose length is > 4
@return Returns TRUE if successful, FALSE otherwise
*/
BOOL
-jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) {
+jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) {
// assume Little Endian order
- BOOL bMotorolaOrder = FALSE;
+ BOOL bBigEndian = FALSE;
// process Exif GPS IFD
- return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_GPS);
+ return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, TagLib::EXIF_GPS);
}
+// ==========================================================
+// Exif common helper routines
+// ==========================================================
+
/**
Rotate a dib according to Exif info
@param dib Input / Output dib to rotate
@@ -943,49 +971,283 @@ RotateExif(FIBITMAP **dib) {
// 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;
+ if((tag != NULL) && (FreeImage_GetTagID(tag) == TAG_ORIENTATION)) {
+ const WORD orientation = *((WORD *)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;
+ }
+ }
+ }
+}
+
+// ==========================================================
+// Exif TIFF JPEG-XR helper routines
+// ==========================================================
+
+class PredicateTagIDCompare {
+public:
+ bool operator()(FITAG *a, FITAG *b) {
+ WORD tag_id_a = FreeImage_GetTagID(a);
+ WORD tag_id_b = FreeImage_GetTagID(b);
+ return (tag_id_a < tag_id_b);
+ }
+};
+
+/**
+Write a metadata model as a TIF IFD to a FIMEMORY handle.
+The entries in the TIF IFD are sorted in ascending order by tag id.
+The last entry is written as 0 (4 bytes) which means no more IFD to follow.
+Supported metadata models are
+<ul>
+<li>FIMD_EXIF_MAIN
+<li>FIMD_EXIF_EXIF
+<li>FIMD_EXIF_GPS
+<li>FIMD_EXIF_INTEROP
+</ul>
+The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset)
+
+@param dib Input FIBITMAP
+@param md_model Metadata model to write
+@param hmem Memory handle
+@return Returns TRUE if successful, FALSE otherwise
+@see tiff_get_ifd_profile
+*/
+static BOOL
+tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) {
+ FITAG *tag = NULL;
+ FIMETADATA *mdhandle = NULL;
+ std::vector<FITAG*> vTagList;
+ TagLib::MDMODEL internal_md_model;
+
+ DWORD ifd_offset = 0; // WORD-aligned IFD value offset
+
+ const BYTE empty_byte = 0;
+
+ // start of the file
+ const long start_of_file = FreeImage_TellMemory(hmem);
+
+ // get the metadata count
+ unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib);
+ if(metadata_count == 0) {
+ return FALSE;
+ }
+
+ TagLib& s = TagLib::instance();
+
+ // check for supported metadata models
+ switch(md_model) {
+ case FIMD_EXIF_MAIN:
+ internal_md_model = TagLib::EXIF_MAIN;
+ break;
+ case FIMD_EXIF_EXIF:
+ internal_md_model = TagLib::EXIF_EXIF;
+ break;
+ case FIMD_EXIF_GPS:
+ internal_md_model = TagLib::EXIF_GPS;
+ break;
+ case FIMD_EXIF_INTEROP:
+ internal_md_model = TagLib::EXIF_INTEROP;
+ break;
+ default:
+ return FALSE;
+ }
+
+ try {
+ // 1) according to the TIFF specifications,
+ // the entries in a TIF IFD must be sorted in ascending order by tag id
+
+ // store the tags into a vector
+ vTagList.reserve(metadata_count);
+ mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag);
+ if(mdhandle) {
+ // parse the tags and store them inside vTagList
+ do {
+ // rewrite the tag id using FreeImage internal database
+ // (in case the tag id is wrong or missing)
+ const char *key = FreeImage_GetTagKey(tag);
+ int tag_id = s.getTagID(internal_md_model, key);
+ if(tag_id != -1) {
+ // this is a known tag, set the tag ID
+ FreeImage_SetTagID(tag, (WORD)tag_id);
+ // record the tag
+ vTagList.push_back(tag);
+ }
+ // else ignore this tag
+ } while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+ FreeImage_FindCloseMetadata(mdhandle);
+
+ // sort the vector by tag id
+ std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare());
+
+ // update the metadata_count
+ metadata_count = (unsigned)vTagList.size();
+
+ } else {
+ throw(1);
+ }
+
+ // 2) prepare the place for each IFD entries.
+
+ /*
+ An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields),
+ followed by a sequence of 12-byte field entries,
+ followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD.
+ */
+
+ {
+ // prepare place for 2 bytes for number of entries + 12 bytes for each entry
+ unsigned ifd_size = 2 + 12 * metadata_count;
+ FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem);
+ // record the offset used to write values > 4-bytes
+ ifd_offset = FreeImage_TellMemory(hmem);
+ // rewind
+ FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET);
+ }
+
+ // 3) write each IFD entry in tag id ascending order
+
+ // number of directory entries
+ WORD nde = (WORD)metadata_count;
+ FreeImage_WriteMemory(&nde, 1, 2, hmem);
+
+ // for each entry ...
+ for(unsigned i = 0; i < metadata_count; i++) {
+ FITAG *tag = vTagList[i];
+ // tag id
+ WORD tag_id = FreeImage_GetTagID(tag);
+ FreeImage_WriteMemory(&tag_id, 1, 2, hmem);
+ // tag type (compliant with TIFF specification)
+ WORD tag_type = (WORD)FreeImage_GetTagType(tag);
+ FreeImage_WriteMemory(&tag_type, 1, 2, hmem);
+ // tag count
+ DWORD tag_count = FreeImage_GetTagCount(tag);
+ FreeImage_WriteMemory(&tag_count, 1, 4, hmem);
+ // tag value or offset (results are in BYTE's units)
+ unsigned tag_length = FreeImage_GetTagLength(tag);
+ if(tag_length <= 4) {
+ // 4 bytes or less, write the value (left justified)
+ const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag);
+ FreeImage_WriteMemory(tag_value, 1, tag_length, hmem);
+ for(unsigned k = tag_length; k < 4; k++) {
+ FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
}
+ } else {
+ // write an offset
+ FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem);
+ // write the value
+ long current_position = FreeImage_TellMemory(hmem);
+ FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
+ FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem);
+ if(tag_length & 1) {
+ // align to the next WORD boundary
+ FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
+ }
+ // next offset to use
+ ifd_offset = FreeImage_TellMemory(hmem);
+ // rewind
+ FreeImage_SeekMemory(hmem, current_position, SEEK_SET);
}
}
+
+ // end-of-IFD or next IFD (0 == none)
+ FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
+ FreeImage_WriteMemory(&empty_byte, 1, 4, hmem);
+
+ return TRUE;
+ }
+ catch(int) {
+ return FALSE;
+ }
+}
+
+/**
+Write a metadata model as a TIF IFD, returns the IFD as a buffer.
+The buffer is allocated by the function and must be freed by the caller, using 'free'.
+@param dib Input FIBITMAP
+@param md_model Metadata model to write
+@param ppbProfile Returned buffer
+@param uProfileLength Returned buffer size
+@return Returns TRUE if successful, FALSE otherwise
+@see tiff_write_ifd
+*/
+BOOL
+tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength) {
+ FIMEMORY *hmem = NULL;
+
+ try {
+ // open a memory stream
+ hmem = FreeImage_OpenMemory(NULL, 0);
+ if(!hmem) {
+ throw(1);
+ }
+
+ // write the metadata model as a TIF IFD
+ BOOL bResult = tiff_write_ifd(dib, md_model, hmem);
+
+ if(bResult) {
+ BYTE *data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hmem, &data, &size_in_bytes);
+
+ // (re-)allocate output buffer
+ BYTE *pbProfile = *ppbProfile;
+ pbProfile = (BYTE*)realloc(pbProfile, size_in_bytes);
+ if(!pbProfile) {
+ throw(1);
+ } else {
+ // copy IFD
+ memcpy(pbProfile, data, size_in_bytes);
+ *ppbProfile = pbProfile;
+ *uProfileLength = size_in_bytes;
+ }
+ }
+
+ // free the memory stream
+ FreeImage_CloseMemory(hmem);
+
+ return bResult;
+
+ } catch(int) {
+ FreeImage_CloseMemory(hmem);
+ return FALSE;
}
}
diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp
index f8af31f9eb..cc12a5d16b 100644
--- a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp
+++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp
@@ -295,11 +295,6 @@ FreeImage_SetTagValue(FITAG *tag, const void *value) {
// FITAG internal helper functions
// --------------------------------------------------------------------------
-/**
-Given a FREE_IMAGE_MDTYPE, calculate the size of this type in bytes unit
-@param type Input data type
-@return Returns the size of the data type, in bytes unit
-*/
unsigned
FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) {
static const unsigned format_bytes[] = {
@@ -328,3 +323,31 @@ FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) {
format_bytes[type] : 0;
}
+size_t
+FreeImage_GetTagMemorySize(FITAG *tag) {
+ size_t size = 0;
+ if (tag) {
+ FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+ size += sizeof(FITAG);
+ size += sizeof(FITAGHEADER);
+ if (tag_header->key) {
+ size += strlen(tag_header->key) + 1;
+ }
+ if (tag_header->description) {
+ size += strlen(tag_header->description) + 1;
+ }
+ if (tag_header->value) {
+ switch (tag_header->type) {
+ case FIDT_ASCII:
+ // for ASCII strings, the value of the count part of an ASCII tag entry includes the NULL.
+ // however, FreeImage adds another '\0' to be sure that this last character is present.
+ size += tag_header->length + 1;
+ break;
+ default:
+ size += tag_header->length;
+ break;
+ }
+ }
+ }
+ return size;
+}
diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.h b/plugins/AdvaImg/src/Metadata/FreeImageTag.h
index f9021ace27..beb8bc84b2 100644
--- a/plugins/AdvaImg/src/Metadata/FreeImageTag.h
+++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.h
@@ -291,14 +291,22 @@
// Helper functions to deal with the FITAG structure
// --------------------------------------------------------------------------
-/**
+/**
Describes the tag format descriptor
+Given a FREE_IMAGE_MDTYPE, calculate the size of this type in bytes unit
@param type Tag data type
-@return Returns the width of a single element, in bytes
+@return Returns the size of the data type, in bytes
@see FREE_IMAGE_MDTYPE
*/
unsigned FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type);
+/**
+Calculate the memory size required by a tag, including the size of the structure
+@param tag The tag to examine
+@return Retuns the memory size used by a tag
+*/
+size_t FreeImage_GetTagMemorySize(FITAG *tag);
+
// --------------------------------------------------------------------------
/**
@@ -467,13 +475,18 @@ static const char *g_TagLib_ExifRawFieldName = "ExifRaw";
extern "C" {
#endif
-// JPEG Exif profile
+// JPEG / JPEG-XR Exif profile (see Exif.cpp)
+// --------------------------------------------------------------------------
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);
+BOOL jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset);
+BOOL jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset);
+
+BOOL tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength);
-// JPEG / TIFF IPTC profile
+
+// JPEG / TIFF IPTC profile (see IPTC.cpp)
+// --------------------------------------------------------------------------
BOOL read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen);
BOOL write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size);
diff --git a/plugins/AdvaImg/src/Metadata/XTIFF.cpp b/plugins/AdvaImg/src/Metadata/XTIFF.cpp
index 681eed12c5..d5be902ad4 100644
--- a/plugins/AdvaImg/src/Metadata/XTIFF.cpp
+++ b/plugins/AdvaImg/src/Metadata/XTIFF.cpp
@@ -48,14 +48,14 @@
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! **/
+ { TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoPixelScale" },
+ { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"Intergraph TransformationMatrix" },
+ { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTransformationMatrix" },
+ { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTiePoints" },
+ { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoKeyDirectory" },
+ { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoDoubleParams" },
+ { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, (char*) "GeoASCIIParams" },
+ { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, (char*)"JPL Carto IFD offset" } /** Don't use this! **/
};
static void
@@ -82,19 +82,22 @@ _XTIFFDefaultDirectory(TIFF *tif) {
the default directory method, we call it now to
allow it to set up the rest of its own methods.
*/
- if (_ParentExtender)
+ if (_ParentExtender) {
(*_ParentExtender)(tif);
+ }
}
/**
-XTIFF Initializer -- sets up the callback procedure for the TIFF module
+XTIFF Initializer -- sets up the callback procedure for the TIFF module.
+@see PluginTIFF::InitTIFF
*/
void
XTIFFInitialize(void) {
static int first_time = 1;
- if (! first_time)
+ if (! first_time) {
return; /* Been there. Done that. */
+ }
first_time = 0;
// Grab the inherited method and install
@@ -105,15 +108,28 @@ XTIFFInitialize(void) {
// GeoTIFF tag reading / writing
// ----------------------------------------------------------
-void
+BOOL
tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
char defaultKey[16];
- size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
+ // first check for a mandatory tag
+ {
+ short tag_count = 0;
+ void* data = NULL;
+
+ if(!TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tag_count, &data)) {
+ // no GeoTIFF tag here
+ return TRUE;
+ }
+ }
+
+ // next, read GeoTIFF tags
+
+ const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
TagLib& tag_lib = TagLib::instance();
- for(unsigned i = 0; i < tag_size; i++) {
+ for(size_t i = 0; i < tag_size; i++) {
const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
@@ -123,8 +139,9 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
if(TIFFGetField(tif, fieldInfo->field_tag, &params)) {
// create a tag
FITAG *tag = FreeImage_CreateTag();
- if(!tag)
- return;
+ if(!tag) {
+ return FALSE;
+ }
WORD tag_id = (WORD)fieldInfo->field_tag;
@@ -147,8 +164,9 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
if(TIFFGetField(tif, fieldInfo->field_tag, &tag_count, &data)) {
// create a tag
FITAG *tag = FreeImage_CreateTag();
- if(!tag)
- return;
+ if(!tag) {
+ return FALSE;
+ }
WORD tag_id = (WORD)fieldInfo->field_tag;
FREE_IMAGE_MDTYPE tag_type = (FREE_IMAGE_MDTYPE)fieldInfo->field_type;
@@ -167,21 +185,24 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
}
}
} // for(tag_size)
+
+ return TRUE;
}
-void
+BOOL
tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
char defaultKey[16];
if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) {
- return;
+ // no GeoTIFF tag here
+ return TRUE;
}
- size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
+ const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
TagLib& tag_lib = TagLib::instance();
- for(unsigned i = 0; i < tag_size; i++) {
+ for(size_t i = 0; i < tag_size; i++) {
const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
FITAG *tag = NULL;
@@ -195,93 +216,133 @@ tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
}
}
}
+
+ return TRUE;
}
// ----------------------------------------------------------
-// EXIF tag reading & writing
+// TIFF EXIF tag reading & writing
// ----------------------------------------------------------
/**
-Read a single exif tag
+Read a single Exif tag
+
+@param tif TIFF handle
+@param tag_id TIFF Tag ID
+@param dib Image being read
+@param md_model Metadata model where to store the tag
+@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL
-tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& tagLib, TIFFDirectory *td, uint32 tag) {
- const TIFFField *fip;
- uint32 value_count;
+tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) {
+ uint32 value_count = 0;
int mem_alloc = 0;
void *raw_data = NULL;
- if(tag == TIFFTAG_EXIFIFD) {
+ if(tag_id == TIFFTAG_EXIFIFD) {
+ // Exif IFD offset - skip this tag
+ // md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model
return TRUE;
}
+ if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) {
+ // Exif GPS IFD offset - skip this tag
+ // should be processed in another way ...
+ return TRUE;
+ }
+
+ TagLib& tagLib = TagLib::instance();
// get the tag key - use NULL to avoid reading GeoTIFF tags
- const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL);
+ const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL);
if(key == NULL) {
return TRUE;
}
- fip = TIFFFieldWithTag(tif, tag);
+ const TIFFField *fip = TIFFFieldWithTag(tif, tag_id);
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"
+ if(TIFFFieldPassCount(fip)) {
+ // a count value is required for 'TIFFGetField'
- // assume TIFF_VARIABLE (uses SHORT count)
- uint16 value_count16;
- if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) {
+ if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) {
+ // a count is required, it will be of type uint16
+ uint16 value_count16 = 0;
+ if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) {
+ // stop, ignore error
return TRUE;
}
value_count = value_count16;
} else {
- if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) {
+ // a count is required, it will be of type uint32
+ uint32 value_count32 = 0;
+ if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) {
+ // stop, ignore error
return TRUE;
}
+ value_count = value_count32;
}
- } else {
+ } else {
// determine count
- if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) {
+ if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) {
value_count = 1;
- } else if (fip->field_readcount == TIFF_SPP) {
- value_count = td->td_samplesperpixel;
+ } else if (TIFFFieldReadCount(fip) == TIFF_SPP) {
+ uint16 spp;
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+ value_count = spp;
} else {
- value_count = fip->field_readcount;
+ value_count = TIFFFieldReadCount(fip);
}
// access fields as pointers to data
// (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code)
- if(fip->field_tag == TIFFTAG_TRANSFERFUNCTION) {
+ if(TIFFFieldTag(fip) == 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
+ if ((TIFFFieldDataType(fip) == TIFF_ASCII
+ || TIFFFieldReadCount(fip) == TIFF_VARIABLE
+ || TIFFFieldReadCount(fip) == TIFF_VARIABLE2
+ || TIFFFieldReadCount(fip) == 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
+ && TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER
+ && TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS
+ && TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING
+ && TIFFFieldTag(fip) != 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
+ && TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE //<- these two are tricky -
+ && TIFFFieldTag(fip) != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value
) {
- if(TIFFGetField(tif, tag, &raw_data) != 1) {
+ if(TIFFGetField(tif, tag_id, &raw_data) != 1) {
+ // stop, ignore error
return TRUE;
}
} else {
+ int value_size = 0;
// access fields as values
- const int value_size = _TIFFDataSize(fip->field_type);
+ // Note:
+ // For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals.
+ {
+ TIFFDataType tag_type = TIFFFieldDataType(fip);
+ switch(tag_type) {
+ case TIFF_RATIONAL:
+ case TIFF_SRATIONAL:
+ value_size = 4;
+ break;
+ default:
+ value_size = TIFFDataWidth(tag_type);
+ break;
+ }
+ }
+
raw_data = _TIFFmalloc(value_size * value_count);
mem_alloc = 1;
int ok = FALSE;
@@ -291,18 +352,18 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
switch(value_count)
{
case 1:
- ok = TIFFGetField(tif, tag, raw_data);
+ ok = TIFFGetField(tif, tag_id, raw_data);
break;
case 2:
- ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1);
+ ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1);
break;
/* # we might need more in the future:
case 3:
- ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2);
+ ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2);
break;
*/
default:
- FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", fip->field_name);
+ FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip));
break;
}
if(ok != 1) {
@@ -322,62 +383,62 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
return FALSE;
}
- FreeImage_SetTagID(fitag, (WORD)tag);
+ FreeImage_SetTagID(fitag, (WORD)tag_id);
FreeImage_SetTagKey(fitag, key);
- switch(fip->field_type) {
+ switch(TIFFFieldDataType(fip)) {
case TIFF_BYTE:
FreeImage_SetTagType(fitag, FIDT_BYTE);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_UNDEFINED:
FreeImage_SetTagType(fitag, FIDT_UNDEFINED);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SBYTE:
FreeImage_SetTagType(fitag, FIDT_SBYTE);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SHORT:
FreeImage_SetTagType(fitag, FIDT_SHORT);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SSHORT:
FreeImage_SetTagType(fitag, FIDT_SSHORT);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_LONG:
FreeImage_SetTagType(fitag, FIDT_LONG);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_IFD:
FreeImage_SetTagType(fitag, FIDT_IFD);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SLONG:
FreeImage_SetTagType(fitag, FIDT_SLONG);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
@@ -392,7 +453,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
rvalue[2*i+1] = rational.getDenominator();
}
FreeImage_SetTagType(fitag, FIDT_RATIONAL);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, rvalue);
free(rvalue);
@@ -409,7 +470,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
rvalue[2*i+1] = rational.getDenominator();
}
FreeImage_SetTagType(fitag, FIDT_RATIONAL);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, rvalue);
free(rvalue);
@@ -418,35 +479,35 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
case TIFF_FLOAT:
FreeImage_SetTagType(fitag, FIDT_FLOAT);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_DOUBLE:
FreeImage_SetTagType(fitag, FIDT_DOUBLE);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_LONG8: // BigTIFF 64-bit unsigned integer
FreeImage_SetTagType(fitag, FIDT_LONG8);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset)
FreeImage_SetTagType(fitag, FIDT_IFD8);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
case TIFF_SLONG8: // BigTIFF 64-bit signed integer
FreeImage_SetTagType(fitag, FIDT_SLONG8);
- FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count);
+ FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
FreeImage_SetTagCount(fitag, value_count);
FreeImage_SetTagValue(fitag, raw_data);
break;
@@ -454,7 +515,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
case TIFF_ASCII:
default: {
size_t length = 0;
- if(!mem_alloc && (fip->field_type == TIFF_ASCII) && (fip->field_readcount == TIFF_VARIABLE)) {
+ if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) {
// when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE),
// tiff_read_exif_tag function gives length of 1 so all strings are truncated ...
// ... try to avoid this by using an explicit calculation for 'length'
@@ -462,7 +523,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
}
else {
// remember that raw_data = _TIFFmalloc(value_size * value_count);
- const int value_size = _TIFFDataSize(fip->field_type);
+ const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) );
length = value_size * value_count;
}
FreeImage_SetTagType(fitag, FIDT_ASCII);
@@ -473,7 +534,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
break;
}
- const char *description = tagLib.getTagDescription(md_model, (WORD)tag);
+ const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id);
if(description) {
FreeImage_SetTagDescription(fitag, description);
}
@@ -491,21 +552,22 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t
/**
Read all known exif tags
+
+@param tif TIFF handle
+@param md_model Metadata model where to store the tags
+@param dib Image being read
+@return Returns TRUE if successful, returns FALSE otherwise
*/
BOOL
tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
- 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);
+ const int count = TIFFGetTagListCount(tif);
+ for(int i = 0; i < count; i++) {
+ uint32 tag_id = TIFFGetTagListEntry(tif, i);
// read the tag
- if (!tiff_read_exif_tag(tif, md_model, dib, tagLib, td, tag))
+ if (!tiff_read_exif_tag(tif, tag_id, dib, md_model))
return FALSE;
}
@@ -514,22 +576,26 @@ tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
// loop over all Core Directory Tags
// ### uses private data, but there is no other way
if(md_model == TagLib::EXIF_MAIN) {
+ const TIFFDirectory *td = &tif->tif_dir;
uint32 lastTag = 0; //<- used to prevent reading some tags twice (as stored in tif_fieldinfo)
for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
const TIFFField *fld = tif->tif_fields[fi];
- if(fld->field_tag == lastTag)
+ const uint32 tag_id = TIFFFieldTag(fld);
+
+ if(tag_id == lastTag) {
continue;
+ }
// test if tag value is set
- // (lifted directly form LibTiff _TIFFWriteDirectory)
+ // (lifted directly from LibTiff _TIFFWriteDirectory)
if( fld->field_bit == FIELD_CUSTOM ) {
- int ci, is_set = FALSE;
+ int is_set = FALSE;
- for( ci = 0; ci < td->td_customValueCount; ci++ ) {
+ for(int ci = 0; ci < td->td_customValueCount; ci++ ) {
is_set |= (td->td_customValues[ci].info == fld);
}
@@ -543,16 +609,14 @@ tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
// process *all* other tags (some will be ignored)
- tiff_read_exif_tag(tif, md_model, dib, tagLib, td, fld->field_tag);
-
+ tiff_read_exif_tag(tif, tag_id, dib, md_model);
- lastTag = fld->field_tag;
+ lastTag = tag_id;
}
}
return TRUE;
-
}
@@ -562,27 +626,47 @@ 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_SUBFILETYPE:
+ case TIFFTAG_OSUBFILETYPE:
case TIFFTAG_IMAGEWIDTH:
case TIFFTAG_IMAGELENGTH:
- case TIFFTAG_SAMPLESPERPIXEL:
case TIFFTAG_BITSPERSAMPLE:
+ case TIFFTAG_COMPRESSION:
case TIFFTAG_PHOTOMETRIC:
- case TIFFTAG_PLANARCONFIG:
+ case TIFFTAG_THRESHHOLDING:
+ case TIFFTAG_CELLWIDTH:
+ case TIFFTAG_CELLLENGTH:
+ case TIFFTAG_FILLORDER:
+ case TIFFTAG_STRIPOFFSETS:
+ case TIFFTAG_ORIENTATION:
+ case TIFFTAG_SAMPLESPERPIXEL:
case TIFFTAG_ROWSPERSTRIP:
case TIFFTAG_STRIPBYTECOUNTS:
- case TIFFTAG_STRIPOFFSETS:
- case TIFFTAG_RESOLUTIONUNIT:
+ case TIFFTAG_MINSAMPLEVALUE:
+ case TIFFTAG_MAXSAMPLEVALUE:
case TIFFTAG_XRESOLUTION:
case TIFFTAG_YRESOLUTION:
- case TIFFTAG_SUBFILETYPE:
+ case TIFFTAG_PLANARCONFIG:
+ case TIFFTAG_FREEOFFSETS:
+ case TIFFTAG_FREEBYTECOUNTS:
+ case TIFFTAG_GRAYRESPONSEUNIT:
+ case TIFFTAG_GRAYRESPONSECURVE:
+ case TIFFTAG_GROUP3OPTIONS:
+ case TIFFTAG_GROUP4OPTIONS:
+ case TIFFTAG_RESOLUTIONUNIT:
case TIFFTAG_PAGENUMBER:
- case TIFFTAG_COLORMAP:
- case TIFFTAG_ORIENTATION:
- case TIFFTAG_COMPRESSION:
+ case TIFFTAG_COLORRESPONSEUNIT:
case TIFFTAG_PREDICTOR:
- case TIFFTAG_GROUP3OPTIONS:
- case TIFFTAG_FILLORDER:
+ case TIFFTAG_COLORMAP:
+ case TIFFTAG_HALFTONEHINTS:
+ case TIFFTAG_TILEWIDTH:
+ case TIFFTAG_TILELENGTH:
+ case TIFFTAG_TILEOFFSETS:
+ case TIFFTAG_TILEBYTECOUNTS:
+ case TIFFTAG_EXTRASAMPLES:
+ case TIFFTAG_SAMPLEFORMAT:
+ case TIFFTAG_SMINSAMPLEVALUE:
+ case TIFFTAG_SMAXSAMPLEVALUE:
// skip always, values have been set in SaveOneTIFF()
return TRUE;
break;
@@ -618,6 +702,11 @@ skip_write_field(TIFF* tif, uint32 tag) {
/**
Write all known exif tags
+
+@param tif TIFF handle
+@param md_model Metadata model from where to load the tags
+@param dib Image being written
+@return Returns TRUE if successful, returns FALSE otherwise
*/
BOOL
tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
@@ -636,19 +725,21 @@ tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
const TIFFField *fld = tif->tif_fields[fi];
+
+ const uint32 tag_id = TIFFFieldTag(fld);
- if(skip_write_field(tif, fld->field_tag)) {
+ if(skip_write_field(tif, tag_id)) {
// skip tags that are already handled by the LibTIFF writing process
continue;
}
FITAG *tag = NULL;
// get the tag key
- const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)fld->field_tag, defaultKey);
+ const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey);
if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
- TIFFDataType tif_tag_type = fld->field_type;
+ TIFFDataType tif_tag_type = TIFFFieldDataType(fld);
// check for identical formats
@@ -658,15 +749,15 @@ tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
continue;
}
// type of storage may differ (e.g. rationnal array vs float array type)
- if(_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) {
+ if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) {
// skip tag or _TIFFmemcpy will fail
continue;
}
if(tag_type == FIDT_ASCII) {
- TIFFSetField(tif, fld->field_tag, FreeImage_GetTagValue(tag));
+ TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag));
} else {
- TIFFSetField(tif, fld->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
+ TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
}
}
}