diff options
Diffstat (limited to 'plugins/AdvaImg/src/Metadata/Exif.cpp')
-rw-r--r-- | plugins/AdvaImg/src/Metadata/Exif.cpp | 1253 |
1 files changed, 0 insertions, 1253 deletions
diff --git a/plugins/AdvaImg/src/Metadata/Exif.cpp b/plugins/AdvaImg/src/Metadata/Exif.cpp deleted file mode 100644 index 35d83c4dbc..0000000000 --- a/plugins/AdvaImg/src/Metadata/Exif.cpp +++ /dev/null @@ -1,1253 +0,0 @@ -// ========================================================== -// Metadata functions implementation -// Exif metadata model -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// - Mihail Naydenov (mnaydenov@users.sourceforge.net) -// -// Based on the following implementations: -// - metadata-extractor : http://www.drewnoakes.com/code/exif/ -// - jhead : http://www.sentex.net/~mwandel/jhead/ -// - ImageMagick : http://www.imagemagick.org/ -// -// 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" - -// ========================================================== -// Exif JPEG routines -// ========================================================== - -#define EXIF_NUM_FORMATS 12 - -#define TAG_EXIF_OFFSET 0x8769 // Exif IFD Pointer -#define TAG_GPS_OFFSET 0x8825 // GPS Info IFD Pointer -#define TAG_INTEROP_OFFSET 0xA005 // Interoperability IFD Pointer -#define TAG_MAKER_NOTE 0x927C // Maker note - -// CANON cameras have some funny bespoke fields that need further processing... -#define TAG_CANON_CAMERA_STATE_0x01 0x0001 // tags under tag 0x001 (CameraSettings) -#define TAG_CANON_CAMERA_STATE_0x02 0x0002 // tags under tag 0x002 (FocalLength) -#define TAG_CANON_CAMERA_STATE_0x04 0x0004 // tags under tag 0x004 (ShotInfo) -#define TAG_CANON_CAMERA_STATE_0x12 0x0012 // tags under tag 0x012 (AFInfo) -#define TAG_CANON_CAMERA_STATE_0xA0 0x00A0 // tags under tag 0x0A0 (ProcessingInfo) -#define TAG_CANON_CAMERA_STATE_0xE0 0x00E0 // tags under tag 0x0E0 (SensorInfo) - - -// ===================================================================== -// Reimplementation of strnicmp (it is not supported on some systems) -// ===================================================================== - -/** -Compare characters of two strings without regard to case. -@param s1 Null-terminated string to compare. -@param s2 Null-terminated string to compare. -@param len Number of characters to compare -@return Returns 0 if s1 substring identical to s2 substring -*/ -static int -FreeImage_strnicmp(const char *s1, const char *s2, size_t len) { - unsigned char c1, c2; - - if(!s1 || !s2) return -1; - - c1 = 0; c2 = 0; - if(len) { - do { - c1 = *s1; c2 = *s2; - s1++; s2++; - if (!c1) - break; - if (!c2) - break; - if (c1 == c2) - continue; - c1 = (BYTE)tolower(c1); - c2 = (BYTE)tolower(c2); - if (c1 != c2) - break; - } while (--len); - } - return (int)c1 - (int)c2; -} - - -// ---------------------------------------------------------- -// Little Endian / Big Endian io routines -// ---------------------------------------------------------- - -static short -ReadInt16(BOOL msb_order, const void *buffer) { - short value; - - if(msb_order) { - value = (short)((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]); - return value; - } - value = (short)((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]); - return value; -} - -static LONG -ReadInt32(BOOL msb_order, const void *buffer) { - LONG value; - - if(msb_order) { - value = (LONG)((((BYTE*) buffer)[0] << 24) | (((BYTE*) buffer)[1] << 16) | (((BYTE*) buffer)[2] << 8) | (((BYTE*) buffer)[3])); - return value; - } - value = (LONG)((((BYTE*) buffer)[3] << 24) | (((BYTE*) buffer)[2] << 16) | (((BYTE*) buffer)[1] << 8 ) | (((BYTE*) buffer)[0])); - return value; -} - -static WORD -ReadUint16(BOOL msb_order, const void *buffer) { - WORD value; - - if(msb_order) { - value = (WORD) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]); - return value; - } - value = (WORD) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]); - return value; -} - -static DWORD -ReadUint32(BOOL msb_order, const void *buffer) { - return ((DWORD) ReadInt32(msb_order, buffer) & 0xFFFFFFFF); -} - -// ---------------------------------------------------------- -// Exif JPEG markers routines -// ---------------------------------------------------------- - -/** -Process a IFD offset -Returns the offset and the metadata model for this tag -*/ -static void -processIFDOffset(FITAG *tag, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { - // get the IFD offset - *subdirOffset = ReadUint32(msb_order, pval); - - // select a tag info table - switch(FreeImage_GetTagID(tag)) { - case TAG_EXIF_OFFSET: - *md_model = TagLib::EXIF_EXIF; - break; - case TAG_GPS_OFFSET: - *md_model = TagLib::EXIF_GPS; - break; - case TAG_INTEROP_OFFSET: - *md_model = TagLib::EXIF_INTEROP; - break; - } -} - -/** -Process a maker note IFD offset -Returns the offset and the metadata model for this tag -*/ -static void -processMakerNote(FIBITMAP *dib, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { - FITAG *tagMake = NULL; - - *subdirOffset = 0; - *md_model = TagLib::UNKNOWN; - - // Determine the camera model and makernote format - // WARNING: note that Maker may be NULL sometimes so check its value before using it - // (NULL pointer checking is done by FreeImage_strnicmp) - FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "Make", &tagMake); - const char *Maker = (char*)FreeImage_GetTagValue(tagMake); - - if((memcmp("OLYMP\x00\x01", pval, 7) == 0) || (memcmp("OLYMP\x00\x02", pval, 7) == 0) || (memcmp("EPSON", pval, 5) == 0) || (memcmp("AGFA", pval, 4) == 0)) { - // Olympus Type 1 Makernote - // Epson and Agfa use Olympus maker note standard, - // see: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ - *md_model = TagLib::EXIF_MAKERNOTE_OLYMPUSTYPE1; - *subdirOffset = 8; - } - else if(memcmp("OLYMPUS\x00\x49\x49\x03\x00", pval, 12) == 0) { - // Olympus Type 2 Makernote - // !!! NOT YET SUPPORTED !!! - *subdirOffset = 0; - *md_model = TagLib::UNKNOWN; - } - else if(memcmp("Nikon", pval, 5) == 0) { - /* There are two scenarios here: - * Type 1: - * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... - * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ - * Type 3: - * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... - * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 - */ - if (pval[6] == 1) { - // Nikon type 1 Makernote - *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE1; - *subdirOffset = 8; - } else if (pval[6] == 2) { - // Nikon type 3 Makernote - *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE3; - *subdirOffset = 18; - } else { - // Unsupported makernote data ignored - *subdirOffset = 0; - *md_model = TagLib::UNKNOWN; - } - } else if(Maker && (FreeImage_strnicmp("NIKON", Maker, 5) == 0)) { - // Nikon type 2 Makernote - *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE2; - *subdirOffset = 0; - } else if(Maker && (FreeImage_strnicmp("Canon", Maker, 5) == 0)) { - // Canon Makernote - *md_model = TagLib::EXIF_MAKERNOTE_CANON; - *subdirOffset = 0; - } else if(Maker && (FreeImage_strnicmp("Casio", Maker, 5) == 0)) { - // Casio Makernote - if(memcmp("QVC\x00\x00\x00", pval, 6) == 0) { - // Casio Type 2 Makernote - *md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE2; - *subdirOffset = 6; - } else { - // Casio Type 1 Makernote - *md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE1; - *subdirOffset = 0; - } - } else if ((memcmp("FUJIFILM", pval, 8) == 0) || (Maker && (FreeImage_strnicmp("Fujifilm", Maker, 8) == 0))) { - // Fujifile Makernote - // 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 = 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) { - *md_model = TagLib::EXIF_MAKERNOTE_KYOCERA; - *subdirOffset = 22; - } - else if(Maker && (FreeImage_strnicmp("Minolta", Maker, 7) == 0)) { - // Minolta maker note - *md_model = TagLib::EXIF_MAKERNOTE_MINOLTA; - *subdirOffset = 0; - } - else if(memcmp("Panasonic\x00\x00\x00", pval, 12) == 0) { - // Panasonic maker note - *md_model = TagLib::EXIF_MAKERNOTE_PANASONIC; - *subdirOffset = 12; - } - else if(Maker && (FreeImage_strnicmp("LEICA", Maker, 5) == 0)) { - // Leica maker note - if(memcmp("LEICA\x00\x00\x00", pval, 8) == 0) { - // not yet supported makernote data ignored - *subdirOffset = 0; - *md_model = TagLib::UNKNOWN; - } - } - else if(Maker && ((FreeImage_strnicmp("Pentax", Maker, 6) == 0) || (FreeImage_strnicmp("Asahi", Maker, 5) == 0))) { - // Pentax maker note - if(memcmp("AOC\x00", pval, 4) == 0) { - // Type 2 Pentax Makernote - *md_model = TagLib::EXIF_MAKERNOTE_PENTAX; - *subdirOffset = 6; - } else { - // Type 1 Pentax Makernote - *md_model = TagLib::EXIF_MAKERNOTE_ASAHI; - *subdirOffset = 0; - } - } - else if((memcmp("SONY CAM\x20\x00\x00\x00", pval, 12) == 0) || (memcmp("SONY DSC\x20\x00\x00\x00", pval, 12) == 0)) { - *md_model = TagLib::EXIF_MAKERNOTE_SONY; - *subdirOffset = 12; - } - else if((memcmp("SIGMA\x00\x00\x00", pval, 8) == 0) || (memcmp("FOVEON\x00\x00", pval, 8) == 0)) { - FITAG *tagModel = NULL; - FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "Model", &tagModel); - const char *Model = (char*)FreeImage_GetTagValue(tagModel); - if(Model && (memcmp("SIGMA SD1\x00", Model, 10) == 0)) { - // Sigma SD1 maker note - *subdirOffset = 10; - *md_model = TagLib::EXIF_MAKERNOTE_SIGMA_SD1; - } else { - // Sigma / Foveon makernote - *subdirOffset = 10; - *md_model = TagLib::EXIF_MAKERNOTE_SIGMA_FOVEON; - } - } -} - -/** -Process a Canon maker note tag. -A single Canon tag may contain many other tags within. -*/ -static BOOL -processCanonMakerNoteTag(FIBITMAP *dib, FITAG *tag) { - char defaultKey[16]; - DWORD startIndex = 0; - TagLib& s = TagLib::instance(); - - WORD tag_id = FreeImage_GetTagID(tag); - - int subTagTypeBase = 0; - - switch(tag_id) { - case TAG_CANON_CAMERA_STATE_0x01: - subTagTypeBase = 0xC100; - startIndex = 1; - break; - case TAG_CANON_CAMERA_STATE_0x02: - subTagTypeBase = 0xC200; - startIndex = 0; - break; - case TAG_CANON_CAMERA_STATE_0x04: - subTagTypeBase = 0xC400; - startIndex = 1; - break; - case TAG_CANON_CAMERA_STATE_0x12: - subTagTypeBase = 0x1200; - startIndex = 0; - break; - case TAG_CANON_CAMERA_STATE_0xA0: - subTagTypeBase = 0xCA00; - startIndex = 1; - break; - case TAG_CANON_CAMERA_STATE_0xE0: - subTagTypeBase = 0xCE00; - startIndex = 1; - break; - - default: - { - // process as a normal tag - - // get the tag key and description - const char *key = s.getTagFieldName(TagLib::EXIF_MAKERNOTE_CANON, tag_id, defaultKey); - FreeImage_SetTagKey(tag, key); - const char *description = s.getTagDescription(TagLib::EXIF_MAKERNOTE_CANON, tag_id); - FreeImage_SetTagDescription(tag, description); - - // store the tag - if(key) { - FreeImage_SetMetadata(FIMD_EXIF_MAKERNOTE, dib, key, tag); - } - - return TRUE; - } - break; - - } - - WORD *pvalue = (WORD*)FreeImage_GetTagValue(tag); - - // create a tag - FITAG *canonTag = FreeImage_CreateTag(); - if(!canonTag) return FALSE; - - // we intentionally skip the first array member (if needed) - for (DWORD i = startIndex; i < FreeImage_GetTagCount(tag); i++) { - - tag_id = (WORD)(subTagTypeBase + i); - - FreeImage_SetTagID(canonTag, tag_id); - FreeImage_SetTagType(canonTag, FIDT_SHORT); - FreeImage_SetTagCount(canonTag, 1); - FreeImage_SetTagLength(canonTag, 2); - FreeImage_SetTagValue(canonTag, &pvalue[i]); - - // get the tag key and description - const char *key = s.getTagFieldName(TagLib::EXIF_MAKERNOTE_CANON, tag_id, defaultKey); - FreeImage_SetTagKey(canonTag, key); - const char *description = s.getTagDescription(TagLib::EXIF_MAKERNOTE_CANON, tag_id); - FreeImage_SetTagDescription(canonTag, description); - - // store the tag - if(key) { - FreeImage_SetMetadata(FIMD_EXIF_MAKERNOTE, dib, key, canonTag); - } - } - - // delete the tag - FreeImage_DeleteTag(canonTag); - - return TRUE; -} - -/** -Process a standard Exif tag -*/ -static void -processExifTag(FIBITMAP *dib, FITAG *tag, char *pval, BOOL msb_order, TagLib::MDMODEL md_model) { - char defaultKey[16]; - int n; - DWORD i; - - // allocate a buffer to store the tag value - BYTE *exif_value = (BYTE*)malloc(FreeImage_GetTagLength(tag) * sizeof(BYTE)); - if(NULL == exif_value) { - // out of memory ... - return; - } - memset(exif_value, 0, FreeImage_GetTagLength(tag) * sizeof(BYTE)); - - // get the tag value - switch(FreeImage_GetTagType(tag)) { - - case FIDT_SHORT: - { - WORD *value = (WORD*)&exif_value[0]; - for(i = 0; i < FreeImage_GetTagCount(tag); i++) { - value[i] = ReadUint16(msb_order, pval + i * sizeof(WORD)); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_SSHORT: - { - short *value = (short*)&exif_value[0]; - for(i = 0; i < FreeImage_GetTagCount(tag); i++) { - value[i] = ReadInt16(msb_order, pval + i * sizeof(short)); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_LONG: - { - DWORD *value = (DWORD*)&exif_value[0]; - for(i = 0; i < FreeImage_GetTagCount(tag); i++) { - value[i] = ReadUint32(msb_order, pval + i * sizeof(DWORD)); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_SLONG: - { - LONG *value = (LONG*)&exif_value[0]; - for(i = 0; i < FreeImage_GetTagCount(tag); i++) { - value[i] = ReadInt32(msb_order, pval + i * sizeof(LONG)); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_RATIONAL: - { - n = sizeof(DWORD); - - DWORD *value = (DWORD*)&exif_value[0]; - for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) { - // read a sequence of (numerator, denominator) - value[i] = ReadUint32(msb_order, n*i + (char*)pval); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_SRATIONAL: - { - n = sizeof(LONG); - - LONG *value = (LONG*)&exif_value[0]; - for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) { - // read a sequence of (numerator, denominator) - value[i] = ReadInt32(msb_order, n*i + (char*)pval); - } - FreeImage_SetTagValue(tag, value); - break; - } - case FIDT_BYTE: - case FIDT_ASCII: - case FIDT_SBYTE: - case FIDT_UNDEFINED: - case FIDT_FLOAT: - case FIDT_DOUBLE: - default: - FreeImage_SetTagValue(tag, pval); - break; - } - - if(md_model == TagLib::EXIF_MAKERNOTE_CANON) { - // A single Canon tag can have multiple values within - processCanonMakerNoteTag(dib, tag); - } - else { - TagLib& s = TagLib::instance(); - - WORD tag_id = FreeImage_GetTagID(tag); - - // get the tag key and description - const char *key = s.getTagFieldName(md_model, tag_id, defaultKey); - FreeImage_SetTagKey(tag, key); - const char *description = s.getTagDescription(md_model, tag_id); - FreeImage_SetTagDescription(tag, description); - - // store the tag - if(key) { - FreeImage_SetMetadata(s.getFreeImageModel(md_model), dib, key, tag); - } - } - - - // free the temporary buffer - free(exif_value); - -} - -/** -Process Exif directory - -@param dib Input FIBITMAP -@param tiffp Pointer to the TIFF header -@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, DWORD dwOffsetIfd0, DWORD dwLength, DWORD dwProfileOffset, BOOL msb_order, TagLib::MDMODEL starting_md_model) { - WORD de, nde; - - std::stack<WORD> destack; // directory entries stack - std::stack<const BYTE*> ifdstack; // IFD stack - std::stack<TagLib::MDMODEL> modelstack; // metadata model stack - - // Keep a list of already visited IFD to avoid stack overflows - // when recursive/cyclic directory structures exist. - // This kind of recursive Exif file was encountered with Kodak images coming from - // KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W - std::map<DWORD, int> visitedIFD; - - /* - "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)." - The "next IFD" (1st IFD) is the thumbnail. - */ - #define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry)) - - // set the metadata model to Exif - - TagLib::MDMODEL md_model = starting_md_model; - - // set the pointer to the first IFD (0th IFD) and follow it were it leads. - - const BYTE *ifd0th = (BYTE*)tiffp + (size_t)dwOffsetIfd0; - - const BYTE *ifdp = ifd0th; - - de = 0; - - do { - // if there is anything on the stack then pop it off - if(!destack.empty()) { - ifdp = ifdstack.top(); ifdstack.pop(); - de = destack.top(); destack.pop(); - md_model = modelstack.top(); modelstack.pop(); - } - - // remember that we've visited this directory and entry so that we don't visit it again later - DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de ); - if(visitedIFD.find(visited) != visitedIFD.end()) { - continue; - } else { - visitedIFD[visited] = 1; // processed - } - - // 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 - char *pval = NULL; // pointer to the tag value - - // create a tag - FITAG *tag = FreeImage_CreateTag(); - if(!tag) return FALSE; - - // point to the directory entry - pde = (char*) DIR_ENTRY_ADDR(ifdp, de); - - // get the tag ID - 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) { - // a problem occured : delete the tag (not free'd after) - FreeImage_DeleteTag(tag); - // break out of the for loop - break; - } - FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type); - - // get number of components - 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) { - FreeImage_DeleteTag(tag); - // jump to next entry - continue; - } - FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * tag_data_width); - - if(FreeImage_GetTagLength(tag) <= 4) { - // 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 - DWORD offset_value = ReadUint32(msb_order, pde + 8); - // 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) > dwLength - offset_value){ - // a problem occured : delete the tag (not free'd after) - FreeImage_DeleteTag(tag); - // jump to next entry - continue; - } - pval = (char*)(tiffp + offset_value); - } - - // check for a IFD offset - BOOL isIFDOffset = FALSE; - switch(FreeImage_GetTagID(tag)) { - case TAG_EXIF_OFFSET: - case TAG_GPS_OFFSET: - case TAG_INTEROP_OFFSET: - case TAG_MAKER_NOTE: - isIFDOffset = TRUE; - break; - } - if(isIFDOffset) { - DWORD sub_offset = 0; - TagLib::MDMODEL next_mdmodel = md_model; - const BYTE *next_ifd = ifdp; - - // get offset and metadata model - if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) { - processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel); - next_ifd = (BYTE*)pval + sub_offset; - } else { - processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel); - next_ifd = (BYTE*)tiffp + sub_offset; - } - - if((sub_offset < dwLength) && (next_mdmodel != TagLib::UNKNOWN)) { - // push our current directory state onto the stack - ifdstack.push(ifdp); - // jump to the next entry - de++; - destack.push(de); - - // push our current metadata model - modelstack.push(md_model); - - // push new state onto of stack to cause a jump - ifdstack.push(next_ifd); - destack.push(0); - - // select a new metadata model - modelstack.push(next_mdmodel); - - // delete the tag as it won't be stored nor deleted in the for() loop - FreeImage_DeleteTag(tag); - - break; // break out of the for loop - } - else { - // unsupported camera model, canon maker tag or something unknown - // process as a standard tag - processExifTag(dib, tag, pval, msb_order, md_model); - } - - } else { - // process as a standard tag - processExifTag(dib, tag, pval, msb_order, md_model); - } - - // delete the tag - FreeImage_DeleteTag(tag); - - } // for(nde) - - // additional thumbnail data is skipped - - } while (!destack.empty()); - - // - // --- handle thumbnail data --- - // - - const WORD entriesCount0th = ReadUint16(msb_order, ifd0th); - - DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th)); - if((next_offset == 0) || (next_offset >= dwLength)) { - return TRUE; //< no thumbnail - } - - const BYTE* const ifd1st = (BYTE*)tiffp + next_offset; - const WORD entriesCount1st = ReadUint16(msb_order, ifd1st); - - unsigned thCompression = 0; - unsigned thOffset = 0; - unsigned thSize = 0; - - for(int e = 0; e < entriesCount1st; e++) { - - // point to the directory entry - const BYTE* base = DIR_ENTRY_ADDR(ifd1st, e); - - // check for buffer overflow - const size_t remaining = (size_t)base + 12 - (size_t)tiffp; - if(remaining >= dwLength) { - // bad IFD1 directory, ignore it - return FALSE; - } - - // get the tag ID - WORD tag = ReadUint16(msb_order, base); - // get the tag type - /*WORD type = */ReadUint16(msb_order, base + sizeof(WORD)); - // get number of components - /*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)); - - switch(tag) { - case TAG_COMPRESSION: - // Tiff Compression Tag (should be COMPRESSION_OJPEG (6), but is not always respected) - thCompression = offset; - break; - case TAG_JPEG_INTERCHANGE_FORMAT: - // Tiff JPEGInterchangeFormat Tag - thOffset = offset; - break; - case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: - // Tiff JPEGInterchangeFormatLength Tag - thSize = offset; - break; - // ### X and Y Resolution ignored, orientation ignored - case TAG_X_RESOLUTION: // XResolution - case TAG_Y_RESOLUTION: // YResolution - case TAG_RESOLUTION_UNIT: // ResolutionUnit - case TAG_ORIENTATION: // Orientation - break; - default: - break; - } - } - - if(/*thCompression != 6 ||*/ thOffset == 0 || thSize == 0) { - return TRUE; - } - - if(thOffset + thSize > dwLength) { - return TRUE; - } - - // load the thumbnail - - const BYTE *thLocation = tiffp + thOffset; - - FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(thLocation), thSize); - FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem); - FreeImage_CloseMemory(hmem); - - // store the thumbnail - FreeImage_SetThumbnail(dib, thumbnail); - // then delete it - FreeImage_Unload(thumbnail); - - return TRUE; -} - -// -------------------------------------------------------------------------- - -/** -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 *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 }; // Classic TIFF signature - little-endian order - BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A }; // Classic TIFF signature - big-endian order - - // 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 - - pbProfile += sizeof(exif_signature); - dwProfileLength -= sizeof(exif_signature); - - // read the TIFF header (8 bytes) - - // check the endianess order - - BOOL bBigEndian = TRUE; - - if(memcmp(pbProfile, lsb_first, sizeof(lsb_first)) == 0) { - // Exif section is in little-endian order - bBigEndian = FALSE; - } else { - 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; - } - } - - // this is the offset to the first IFD (Image File Directory) - DWORD dwFirstOffset = ReadUint32(bBigEndian, pbProfile + 4); - if (dwFirstOffset > dwProfileLength) { - // bad Exif data - return FALSE; - } - - /* - 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 (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"); - return FALSE; - } - */ - - // process Exif directories, starting with Exif-TIFF IFD - 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 -*/ -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; -} - -// ========================================================== -// 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, unsigned file_offset) { - // assume Little Endian order - BOOL bBigEndian = FALSE; - - // process Exif specific IFD - 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 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, unsigned file_offset) { - // assume Little Endian order - BOOL bBigEndian = FALSE; - - // process Exif GPS IFD - 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 -@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) && (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; - } -} |