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) }
/** 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; } }
/** 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)); 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 offset 0th IFD offset @param length Length of the datafile @param msb_order Endianess order of the datafile @return */ static BOOL jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) { WORD de, nde; std::stack<WORD> destack; // directory entries stack std::stack<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; #define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry)) // set the metadata model to Exif TagLib::MDMODEL md_model = TagLib::EXIF_MAIN; // set the pointer to the first IFD and follow it were it leads. BYTE *ifdp = (BYTE*)tiffp + offset; 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 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); 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 FreeImage_SetTagID(tag, ReadUint16(msb_order, pde)); // get the tag format 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 FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4)); // get the size of the tag value in bytes FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth((WORD)FreeImage_GetTagType(tag))); 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 // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data DWORD offset_value = ReadUint32(msb_order, pde + 8); if(offset_value > length) { // a problem occured : delete the tag (not free'd after) FreeImage_DeleteTag(tag); // jump to next entry continue; } // now check if offset + tag length exceeds buffer if(offset_value > length - FreeImage_GetTagLength(tag)) { // 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; 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 < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) { // push our current directory state onto the stack ifdstack.push(ifdp); // bump 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 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()); return TRUE; }
/** 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 { unsigned long *pvalue = (unsigned long *)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) { unsigned long *pvalue = (unsigned long *)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_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 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); }
/** 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: { 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; } return ConvertAnyTag(tag); }
/** 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; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { WebPMux *mux = NULL; FIMEMORY *hmem = NULL; WebPData webp_image; WebPData output_data = { 0 }; WebPMuxError error_status; int copy_data = 1; // 1 : copy data into the mux, 0 : keep a link to local data if(!dib || !handle || !data) { return FALSE; } try { // get the MUX object mux = (WebPMux*)data; if(!mux) { return FALSE; } // --- prepare image data --- // encode image as a WebP blob hmem = FreeImage_OpenMemory(); if(!hmem || !EncodeImage(hmem, dib, flags)) { throw (1); } // store the blob into the mux BYTE *data = NULL; DWORD data_size = 0; FreeImage_AcquireMemory(hmem, &data, &data_size); webp_image.bytes = data; webp_image.size = data_size; error_status = WebPMuxSetImage(mux, &webp_image, copy_data); // no longer needed since copy_data == 1 FreeImage_CloseMemory(hmem); hmem = NULL; if(error_status != WEBP_MUX_OK) { throw (1); } // --- set metadata --- // set ICC color profile { FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if (iccProfile->size && iccProfile->data) { WebPData icc_profile; icc_profile.bytes = (uint8_t*)iccProfile->data; icc_profile.size = (size_t)iccProfile->size; error_status = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); if(error_status != WEBP_MUX_OK) { throw (1); } } } // set XMP metadata { FITAG *tag = NULL; if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag)) { WebPData xmp_profile; xmp_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag); xmp_profile.size = (size_t)FreeImage_GetTagLength(tag); error_status = WebPMuxSetChunk(mux, "XMP ", &xmp_profile, copy_data); if(error_status != WEBP_MUX_OK) { throw (1); } } } // set Exif metadata { FITAG *tag = NULL; if(FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag)) { WebPData exif_profile; exif_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag); exif_profile.size = (size_t)FreeImage_GetTagLength(tag); error_status = WebPMuxSetChunk(mux, "EXIF", &exif_profile, copy_data); if(error_status != WEBP_MUX_OK) { throw (1); } } } // get data from mux in WebP RIFF format error_status = WebPMuxAssemble(mux, &output_data); if(error_status != WEBP_MUX_OK) { FreeImage_OutputMessageProc(s_format_id, "Failed to create webp output file"); throw (1); } // write the file to the output stream if(io->write_proc((void*)output_data.bytes, 1, (unsigned)output_data.size, handle) != output_data.size) { FreeImage_OutputMessageProc(s_format_id, "Failed to write webp output file"); throw (1); } // free WebP output file WebPDataClear(&output_data); return TRUE; } catch(int) { if(hmem) { FreeImage_CloseMemory(hmem); } WebPDataClear(&output_data); return FALSE; } }
BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) { if(!dib) return FALSE; TAGMAP *tagmap = NULL; // get the metadata model METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; METADATAMAP::iterator model_iterator = metadata->find(model); if (model_iterator != metadata->end()) { tagmap = model_iterator->second; } if(key != NULL) { if(!tagmap) { // this model, doesn't exist: create it tagmap = new(std::nothrow) TAGMAP(); (*metadata)[model] = tagmap; } if(tag) { // first check the tag if(FreeImage_GetTagKey(tag) == NULL) { FreeImage_SetTagKey(tag, key); } else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) { // set the tag key FreeImage_SetTagKey(tag, key); } if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) { FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key); return FALSE; } // fill the tag ID if possible and if it's needed TagLib& tag_lib = TagLib::instance(); switch(model) { case FIMD_IPTC: { int id = tag_lib.getTagID(TagLib::IPTC, key); /* if(id == -1) { FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key); } */ FreeImage_SetTagID(tag, (WORD)id); } break; default: break; } // delete existing tag FITAG *old_tag = (*tagmap)[key]; if(old_tag) { FreeImage_DeleteTag(old_tag); } // create a new tag (*tagmap)[key] = FreeImage_CloneTag(tag); } else { // delete existing tag TAGMAP::iterator i = tagmap->find(key); if(i != tagmap->end()) { FITAG *old_tag = (*i).second; FreeImage_DeleteTag(old_tag); tagmap->erase(key); } } } else { // destroy the metadata model if(tagmap) { for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) { FITAG *tag = (*i).second; FreeImage_DeleteTag(tag); } delete tagmap; metadata->erase(model_iterator); } } return TRUE; }
/** Write ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata */ static ERR WriteMetadata(PKImageEncode *pIE, FIBITMAP *dib) { ERR error_code = 0; // error code as returned by the interface BYTE *profile = NULL; unsigned profile_size = 0; try { // write ICC profile { FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if(iccProfile->data) { error_code = pIE->SetColorContext(pIE, (U8*)iccProfile->data, iccProfile->size); JXR_CHECK(error_code); } } // write descriptive metadata if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib)) { error_code = WriteDescriptiveMetadata(pIE, dib); JXR_CHECK(error_code); } // write IPTC metadata if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) { // create a binary profile if(write_iptc_profile(dib, &profile, &profile_size)) { // write the profile error_code = PKImageEncode_SetIPTCNAAMetadata_WMP(pIE, profile, profile_size); JXR_CHECK(error_code); // release profile free(profile); profile = NULL; } } // write XMP metadata { FITAG *tag_xmp = NULL; if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp)) { error_code = PKImageEncode_SetXMPMetadata_WMP(pIE, (BYTE*)FreeImage_GetTagValue(tag_xmp), FreeImage_GetTagLength(tag_xmp)); JXR_CHECK(error_code); } } // write Exif metadata { if(tiff_get_ifd_profile(dib, FIMD_EXIF_EXIF, &profile, &profile_size)) { error_code = PKImageEncode_SetEXIFMetadata_WMP(pIE, profile, profile_size); JXR_CHECK(error_code); // release profile free(profile); profile = NULL; } } // write Exif GPS metadata { if(tiff_get_ifd_profile(dib, FIMD_EXIF_GPS, &profile, &profile_size)) { error_code = PKImageEncode_SetGPSInfoMetadata_WMP(pIE, profile, profile_size); JXR_CHECK(error_code); // release profile free(profile); profile = NULL; } } return WMP_errSuccess; } catch(...) { free(profile); return error_code; } }