void FreeImageGifData::add24bitBGRDataPage(int width, int height, BYTE* pData) { FIBITMAP* newBitmap = FreeImage_Allocate(width, height, 24, 0x0000FF, 0x00FF00, 0xFF0000); BYTE* bitmapData = FreeImage_GetBits(newBitmap); memcpy(bitmapData, pData, width * height * 3); //Set metadata FIBITMAP* convBitmap = FreeImage_ColorQuantizeEx(newBitmap, FIQ_WUQUANT, 256); FITAG* delayTag = FreeImage_CreateTag(); FreeImage_SetMetadata(FIMD_ANIMATION, convBitmap, NULL, NULL); LONG delayVal = 20; if (delayTag) { FreeImage_SetTagKey(delayTag, "FrameTime"); FreeImage_SetTagType(delayTag, FIDT_LONG); FreeImage_SetTagCount(delayTag, 1); FreeImage_SetTagLength(delayTag, 4); FreeImage_SetTagValue(delayTag, &delayVal); FreeImage_SetMetadata(FIMD_ANIMATION, convBitmap, FreeImage_GetTagKey(delayTag), delayTag); FreeImage_DeleteTag(delayTag); } FreeImage_AppendPage(m_gifHandle, convBitmap); int pCount = FreeImage_GetPageCount(m_gifHandle); FreeImage_Unload(newBitmap); FreeImage_Unload(convBitmap); }
// ------------------------------------------------------------ // Keep original size info when using scale option on loading // ------------------------------------------------------------ static void store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) { char buffer[256]; // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { size_t length = 0; // set the original width sprintf(buffer, "%d", (int)width); length = strlen(buffer) + 1; // include the NULL/0 value FreeImage_SetTagKey(tag, "OriginalJPEGWidth"); FreeImage_SetTagLength(tag, (DWORD)length); FreeImage_SetTagCount(tag, (DWORD)length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, buffer); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); // set the original height sprintf(buffer, "%d", (int)height); length = strlen(buffer) + 1; // include the NULL/0 value FreeImage_SetTagKey(tag, "OriginalJPEGHeight"); FreeImage_SetTagLength(tag, (DWORD)length); FreeImage_SetTagCount(tag, (DWORD)length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, buffer); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } }
/** Build and set a FITAG whose type is FIDT_ASCII. The tag must be destroyed by the caller using FreeImage_DeleteTag. @param model Metadata model to be filled @param dib Image to be filled @param key Tag key @param value Tag value @return Returns TRUE if successful, returns FALSE otherwise */ static BOOL mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) { if(!dib || !key || !value) { return FALSE; } // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { BOOL bSuccess = TRUE; // fill the tag DWORD tag_length = (DWORD)(strlen(value) + 1); bSuccess &= FreeImage_SetTagKey(tag, key); bSuccess &= FreeImage_SetTagLength(tag, tag_length); bSuccess &= FreeImage_SetTagCount(tag, tag_length); bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII); bSuccess &= FreeImage_SetTagValue(tag, value); if(bSuccess) { // set the tag FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag); } FreeImage_DeleteTag(tag); return bSuccess; } return FALSE; }
/** Read JPEG_COM marker (comment) */ static BOOL jpeg_read_comment(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { size_t length = datalen; BYTE *profile = (BYTE*)dataptr; // read the comment char *value = (char*)malloc((length + 1) * sizeof(char)); if(value == NULL) return FALSE; memcpy(value, profile, length); value[length] = '\0'; // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { unsigned int count = (unsigned int)length + 1; // includes the null value FreeImage_SetTagID(tag, JPEG_COM); FreeImage_SetTagKey(tag, "Comment"); FreeImage_SetTagLength(tag, count); FreeImage_SetTagCount(tag, count); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, value); // store the tag FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } free(value); return TRUE; }
/** 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; }
FITAG * DLL_CALLCONV FreeImage_CloneTag(FITAG *tag) { if(!tag) return NULL; // allocate a new tag FITAG *clone = FreeImage_CreateTag(); if(!clone) return NULL; try { // copy the tag FITAGHEADER *src_tag = (FITAGHEADER *)tag->data; FITAGHEADER *dst_tag = (FITAGHEADER *)clone->data; // tag ID dst_tag->id = src_tag->id; // tag key if(src_tag->key) { dst_tag->key = (char*)malloc((strlen(src_tag->key) + 1) * sizeof(char)); if(!dst_tag->key) { throw FI_MSG_ERROR_MEMORY; } strcpy(dst_tag->key, src_tag->key); } // tag description if(src_tag->description) { dst_tag->description = (char*)malloc((strlen(src_tag->description) + 1) * sizeof(char)); if(!dst_tag->description) { throw FI_MSG_ERROR_MEMORY; } strcpy(dst_tag->description, src_tag->description); } // tag data type dst_tag->type = src_tag->type; // tag count dst_tag->count = src_tag->count; // tag length dst_tag->length = src_tag->length; // tag value dst_tag->value = (BYTE*)malloc(src_tag->length * sizeof(BYTE)); if(!dst_tag->value) { throw FI_MSG_ERROR_MEMORY; } memcpy(dst_tag->value, src_tag->value, src_tag->length); return clone; } catch(const char *message) { FreeImage_DeleteTag(clone); FreeImage_OutputMessageProc(FIF_UNKNOWN, message); return NULL; } }
void ofxGifEncoder::doSave() { // create a multipage bitmap FIMULTIBITMAP *multi = FreeImage_OpenMultiBitmap(FIF_GIF, ofToDataPath(fileName).c_str(), TRUE, FALSE); FIBITMAP * bmp = NULL; for(int i = 0; i < frames.size(); i++ ) { // we need to swap the channels if we're on little endian (see ofImage::swapRgb); #ifdef TARGET_LITTLE_ENDIAN swapRgb(frames[i]); #endif // get the pixel data bmp = FreeImage_ConvertFromRawBits( frames[i]->pixels, frames[i]->width, frames[i]->height, frames[i]->width*(frames[i]->bitsPerPixel/8), frames[i]->bitsPerPixel, 0, 0, 0, true // in of006 this (topdown) had to be false. ); #ifdef TARGET_LITTLE_ENDIAN swapRgb(frames[i]); #endif DWORD frameDuration = (DWORD) (frames[i]->duration * 1000.f); bmp = FreeImage_ColorQuantizeEx(bmp, FIQ_NNQUANT, nColors); // dithering :) if(ditherMode > OFX_GIF_DITHER_NONE) bmp = FreeImage_Dither(bmp, (FREE_IMAGE_DITHER)ditherMode); // clear any animation metadata used by this dib as we’ll adding our own ones FreeImage_SetMetadata(FIMD_ANIMATION, bmp, NULL, NULL); // add animation tags to dib[i] FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, "FrameTime"); FreeImage_SetTagType(tag, FIDT_LONG); FreeImage_SetTagCount(tag, 1); FreeImage_SetTagLength(tag, 4); FreeImage_SetTagValue(tag, &frameDuration); FreeImage_SetMetadata(FIMD_ANIMATION, bmp, FreeImage_GetTagKey(tag), tag); FreeImage_DeleteTag(tag); } FreeImage_AppendPage(multi, bmp); } FreeImage_Unload(bmp); FreeImage_CloseMultiBitmap(multi); }
FITAG * DLL_CALLCONV FreeImage_CloneTag(FITAG *tag) { if(!tag) return NULL; // allocate a new tag FITAG *clone = FreeImage_CreateTag(); if(!clone) return NULL; // copy the tag FITAGHEADER *src_tag = (FITAGHEADER *)tag->data; FITAGHEADER *dst_tag = (FITAGHEADER *)clone->data; // tag ID dst_tag->id = src_tag->id; // tag key if(src_tag->key) { dst_tag->key = (char*)malloc((strlen(src_tag->key) + 1) * sizeof(char)); strcpy(dst_tag->key, src_tag->key); } // tag description if(src_tag->description) { dst_tag->description = (char*)malloc((strlen(src_tag->description) + 1) * sizeof(char)); strcpy(dst_tag->description, src_tag->description); } // tag data type dst_tag->type = src_tag->type; // tag count dst_tag->count = src_tag->count; // tag length dst_tag->length = src_tag->length; // tag value switch(dst_tag->type) { case FIDT_ASCII: dst_tag->value = (char*)malloc((strlen((char*)src_tag->value) + 1) * sizeof(char)); strcpy((char*)dst_tag->value, (char*)src_tag->value); break; default: dst_tag->value = (BYTE*)malloc(src_tag->length * sizeof(BYTE)); memcpy(dst_tag->value, src_tag->value, src_tag->length); break; } return clone; }
static BOOL ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword const char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; png_textp text_ptr = NULL; int num_text = 0; // iTXt/tEXt/zTXt chuncks if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) { for(int i = 0; i < num_text; i++) { // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length); FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, text_ptr[i].text); if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) { // store the tag as XMP FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); } else { // store the tag as a comment FreeImage_SetTagKey(tag, text_ptr[i].key); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); } // destroy the tag FreeImage_DeleteTag(tag); } } return TRUE; }
/** Read JPEG_APP1 marker (XMP profile) @param dib Input FIBITMAP @param dataptr Pointer to the APP1 marker @param datalen APP1 marker length @return Returns TRUE if successful, FALSE otherwise */ static BOOL jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { // marker identifying string for XMP (null terminated) char *xmp_signature = "http://ns.adobe.com/xap/1.0/"; size_t length = datalen; BYTE *profile = (BYTE*)dataptr; // verify the identifying string if(memcmp(xmp_signature, profile, strlen(xmp_signature)) == 0) { // XMP profile size_t offset = strlen(xmp_signature) + 1; profile += offset; length -= offset; // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagID(tag, JPEG_APP0+1); // 0xFFE1 FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetTagLength(tag, (DWORD)length); FreeImage_SetTagCount(tag, (DWORD)length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, profile); // store the tag FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } return TRUE; } return FALSE; }
static BOOL FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value) { BOOL bResult = FALSE; FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, key); FreeImage_SetTagID(tag, id); FreeImage_SetTagType(tag, type); FreeImage_SetTagCount(tag, count); FreeImage_SetTagLength(tag, length); FreeImage_SetTagValue(tag, value); if(model == FIMD_ANIMATION) { TagLib& s = TagLib::instance(); // get the tag description const char *description = s.getTagDescription(TagLib::ANIMATION, id); FreeImage_SetTagDescription(tag, description); } // store the tag bResult = FreeImage_SetMetadata(model, dib, key, tag); FreeImage_DeleteTag(tag); } return bResult; }
/** Read a single exif tag */ static BOOL tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& tagLib, TIFFDirectory *td, uint32 tag) { const TIFFField *fip; uint32 value_count; int mem_alloc = 0; void *raw_data = NULL; if(tag == TIFFTAG_EXIFIFD) { return TRUE; } // get the tag key - use NULL to avoid reading GeoTIFF tags const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL); if(key == NULL) { return TRUE; } fip = TIFFFieldWithTag(tif, tag); if(fip == NULL) { return TRUE; } if(fip->field_passcount) { //<- "passcount" means "returns count" if (fip->field_readcount != TIFF_VARIABLE2) { //<- TIFF_VARIABLE2 means "uses LONG count" // assume TIFF_VARIABLE (uses SHORT count) uint16 value_count16; if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) { return TRUE; } value_count = value_count16; } else { if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) { return TRUE; } } } else { // determine count if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) { value_count = 1; } else if (fip->field_readcount == TIFF_SPP) { value_count = td->td_samplesperpixel; } else { value_count = fip->field_readcount; } // access fields as pointers to data // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) if(fip->field_tag == TIFFTAG_TRANSFERFUNCTION) { // reading this tag cause a bug probably located somewhere inside libtiff return TRUE; } if ((fip->field_type == TIFF_ASCII || fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2 || fip->field_readcount == TIFF_SPP || value_count > 1) && fip->field_tag != TIFFTAG_PAGENUMBER && fip->field_tag != TIFFTAG_HALFTONEHINTS && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING && fip->field_tag != TIFFTAG_DOTRANGE && fip->field_tag != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - && fip->field_tag != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value ) { if(TIFFGetField(tif, tag, &raw_data) != 1) { return TRUE; } } else { // access fields as values const int value_size = _TIFFDataSize(fip->field_type); raw_data = _TIFFmalloc(value_size * value_count); mem_alloc = 1; int ok = FALSE; // ### if value_count > 1, tag is PAGENUMBER or HALFTONEHINTS or YCBCRSUBSAMPLING or DOTRANGE, // all off which are value_count == 2 (see tif_dirinfo.c) switch(value_count) { case 1: ok = TIFFGetField(tif, tag, raw_data); break; case 2: ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1); break; /* # we might need more in the future: case 3: ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2); break; */ default: FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", fip->field_name); break; } if(ok != 1) { _TIFFfree(raw_data); return TRUE; } } } // build FreeImage tag from Tiff Tag data we collected FITAG *fitag = FreeImage_CreateTag(); if(!fitag) { if(mem_alloc) { _TIFFfree(raw_data); } return FALSE; } FreeImage_SetTagID(fitag, (WORD)tag); FreeImage_SetTagKey(fitag, key); switch(fip->field_type) { case TIFF_BYTE: FreeImage_SetTagType(fitag, FIDT_BYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_UNDEFINED: FreeImage_SetTagType(fitag, FIDT_UNDEFINED); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SBYTE: FreeImage_SetTagType(fitag, FIDT_SBYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SHORT: FreeImage_SetTagType(fitag, FIDT_SHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SSHORT: FreeImage_SetTagType(fitag, FIDT_SSHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG: FreeImage_SetTagType(fitag, FIDT_LONG); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD: FreeImage_SetTagType(fitag, FIDT_IFD); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG: FreeImage_SetTagType(fitag, FIDT_SLONG); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_RATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_SRATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_FLOAT: FreeImage_SetTagType(fitag, FIDT_FLOAT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_DOUBLE: FreeImage_SetTagType(fitag, FIDT_DOUBLE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG8: // BigTIFF 64-bit unsigned integer FreeImage_SetTagType(fitag, FIDT_LONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset) FreeImage_SetTagType(fitag, FIDT_IFD8); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG8: // BigTIFF 64-bit signed integer FreeImage_SetTagType(fitag, FIDT_SLONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; default: { size_t length = strlen((char*)raw_data) + 1; FreeImage_SetTagType(fitag, FIDT_ASCII); FreeImage_SetTagLength(fitag, (DWORD)length); FreeImage_SetTagCount(fitag, (DWORD)length); FreeImage_SetTagValue(fitag, raw_data); } break; } const char *description = tagLib.getTagDescription(md_model, (WORD)tag); if(description) { FreeImage_SetTagDescription(fitag, description); } // store the tag FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag); // destroy the tag FreeImage_DeleteTag(fitag); if(mem_alloc) { _TIFFfree(raw_data); } return TRUE; }
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) }
static BOOL ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword const char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; png_textp text_ptr = NULL; png_timep mod_time = NULL; int num_text = 0; // iTXt/tEXt/zTXt chuncks if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) { for(int i = 0; i < num_text; i++) { // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length); FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, text_ptr[i].text); if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) { // store the tag as XMP FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); } else { // store the tag as a comment FreeImage_SetTagKey(tag, text_ptr[i].key); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); } // destroy the tag FreeImage_DeleteTag(tag); } } // timestamp chunk if(png_get_tIME(png_ptr, info_ptr, &mod_time)) { char timestamp[32]; // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; // convert as 'yyyy:MM:dd hh:mm:ss' sprintf(timestamp, "%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); DWORD tag_length = (DWORD)strlen(timestamp) + 1; FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagID(tag, TAG_DATETIME); FreeImage_SetTagValue(tag, timestamp); // store the tag as Exif-TIFF FreeImage_SetTagKey(tag, "DateTime"); FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } return TRUE; }
/** 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; }
/** 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 = 0xC120; 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; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { WebPMux *mux = NULL; WebPMuxFrameInfo webp_frame = { 0 }; // raw image WebPData color_profile; // ICC raw data WebPData xmp_metadata; // XMP raw data WebPData exif_metadata; // EXIF raw data FIBITMAP *dib = NULL; WebPMuxError error_status; if(!handle) { return NULL; } try { // get the MUX object mux = (WebPMux*)data; if(!mux) { throw (1); } // gets the feature flags from the mux object uint32_t webp_flags = 0; error_status = WebPMuxGetFeatures(mux, &webp_flags); if(error_status != WEBP_MUX_OK) { throw (1); } // get image data error_status = WebPMuxGetFrame(mux, 1, &webp_frame); if(error_status == WEBP_MUX_OK) { // decode the data (can be limited to the header if flags uses FIF_LOAD_NOPIXELS) dib = DecodeImage(&webp_frame.bitstream, flags); if(!dib) { throw (1); } // get ICC profile if(webp_flags & ICCP_FLAG) { error_status = WebPMuxGetChunk(mux, "ICCP", &color_profile); if(error_status == WEBP_MUX_OK) { FreeImage_CreateICCProfile(dib, (void*)color_profile.bytes, (long)color_profile.size); } } // get XMP metadata if(webp_flags & XMP_FLAG) { error_status = WebPMuxGetChunk(mux, "XMP ", &xmp_metadata); if(error_status == WEBP_MUX_OK) { // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetTagLength(tag, (DWORD)xmp_metadata.size); FreeImage_SetTagCount(tag, (DWORD)xmp_metadata.size); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, xmp_metadata.bytes); // store the tag FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } } } // get Exif metadata if(webp_flags & EXIF_FLAG) { error_status = WebPMuxGetChunk(mux, "EXIF", &exif_metadata); if(error_status == WEBP_MUX_OK) { // read the Exif raw data as a blob jpeg_read_exif_profile_raw(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); // read and decode the Exif data jpeg_read_exif_profile(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); } } } WebPDataClear(&webp_frame.bitstream); return dib; } catch(int) { WebPDataClear(&webp_frame.bitstream); return NULL; } }
void ofxGifEncoder::processFrame(ofxGifFrame * frame, FIMULTIBITMAP *multi){ FIBITMAP * bmp = NULL; // we need to swap the channels if we're on little endian (see ofImage::swapRgb); if (frame->bitsPerPixel ==32){ ofLog() << "image is transaprent!"; frame = convertTo24BitsWithGreenScreen(frame); } #ifdef TARGET_LITTLE_ENDIAN swapRgb(frame); #endif // from here on, we can only deal with 24 bits // get the pixel data bmp = FreeImage_ConvertFromRawBits( frame->pixels, frame->width, frame->height, frame->width*(frame->bitsPerPixel/8), frame->bitsPerPixel, 0, 0, 0, true // in of006 this (topdown) had to be false. ); FIBITMAP * bmpConverted; #ifdef TARGET_LITTLE_ENDIAN swapRgb(frame); #endif FIBITMAP * quantizedBmp = NULL; FIBITMAP * ditheredBmp = NULL; FIBITMAP * processedBmp = NULL; quantizedBmp = FreeImage_ColorQuantizeEx(bmp, FIQ_WUQUANT, nColors); processedBmp = quantizedBmp; if (nChannels == 4){ calculatePalette(processedBmp); FreeImage_SetTransparentIndex(processedBmp,getClosestToGreenScreenPaletteColorIndex()); } // dithering :) if(ditherMode > OFX_GIF_DITHER_NONE) { ditheredBmp = FreeImage_Dither(processedBmp, (FREE_IMAGE_DITHER)ditherMode); processedBmp = ditheredBmp; } DWORD frameDuration = (DWORD) (frame->duration * 1000.f); // clear any animation metadata used by this dib as we’ll adding our own ones FreeImage_SetMetadata(FIMD_ANIMATION, processedBmp, NULL, NULL); // add animation tags to dib[frameNum] FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, "FrameTime"); FreeImage_SetTagType(tag, FIDT_LONG); FreeImage_SetTagCount(tag, 1); FreeImage_SetTagLength(tag, 4); FreeImage_SetTagValue(tag, &frameDuration); FreeImage_SetMetadata(FIMD_ANIMATION, processedBmp, FreeImage_GetTagKey(tag), tag); FreeImage_DeleteTag(tag); } FreeImage_AppendPage(multi, processedBmp); // clear freeimage stuff if(bmp != NULL) FreeImage_Unload(bmp); if(quantizedBmp != NULL) FreeImage_Unload(quantizedBmp); if(ditheredBmp != NULL) FreeImage_Unload(ditheredBmp); // no need to unload processedBmp, as it points to either of the above }
//-------------------------------------------------------------- void ofxGifEncoder::save (vector <ofxGifFrame *> frames, string fileName, int nColors) { if (nColors < 2 || nColors > 256) { ofLog(OF_LOG_WARNING, "nColors must be between 2 and 256. your gif won't be saved"); return; } // create a multipage bitmap FIMULTIBITMAP *multi = FreeImage_OpenMultiBitmap(FIF_GIF, ofToDataPath(fileName).c_str(), TRUE, FALSE); FIBITMAP * bmp = NULL; for(int i = 0; i < frames.size(); i++ ) { // we need to swap the channels if we're on little endian (see ofImage::swapRgb); #ifdef TARGET_LITTLE_ENDIAN swapRgb(frames[i]); #endif // get the pixel data bmp = FreeImage_ConvertFromRawBits( frames[i]->pixels, frames[i]->width, frames[i]->height, frames[i]->width*(frames[i]->bitsPerPixel/8), frames[i]->bitsPerPixel, 0, 0, 0, true // in of006 this (topdown) had to be false. ); #ifdef TARGET_LITTLE_ENDIAN swapRgb(frames[i]); #endif DWORD frameDuration = (DWORD) frames[i]->duration * 1000.f; // bmp = FreeImage_ColorQuantize(bmp, FIQ_NNQUANT); // if we want to set a reduced color palette (2 to 256); bmp = FreeImage_ColorQuantizeEx(bmp, FIQ_NNQUANT, nColors); // dithering :) // you can set a different dither pattern for each frame // bmp = FreeImage_Dither(bmp, (FREE_IMAGE_DITHER)((i+1)%6)); //bmp = FreeImage_Dither(bmp, FID_BAYER8x8); // clear any animation metadata used by this dib as we’ll adding our own ones FreeImage_SetMetadata(FIMD_ANIMATION, bmp, NULL, NULL); // add animation tags to dib[i] FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, "FrameTime"); FreeImage_SetTagType(tag, FIDT_LONG); FreeImage_SetTagCount(tag, 1); FreeImage_SetTagLength(tag, 4); FreeImage_SetTagValue(tag, &frameDuration); FreeImage_SetMetadata(FIMD_ANIMATION, bmp, FreeImage_GetTagKey(tag), tag); FreeImage_DeleteTag(tag); } FreeImage_AppendPage(multi, bmp); } FreeImage_Unload(bmp); FreeImage_CloseMultiBitmap(multi); }
/** 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<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 = TagLib::EXIF_MAIN; // set the pointer to the first IFD (0th IFD) and follow it were it leads. const BYTE *ifd0th = (BYTE*)tiffp + offset; 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); 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 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 FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4)); // 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 // 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 that length does not exceed the buffer size if(FreeImage_GetTagLength(tag) > length - 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 < (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 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 >= length)) { 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 >= length) { // 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 > length) { 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 ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata @see ReadProfile, ReadDescriptiveMetadata */ static ERR ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { ERR error_code = 0; // error code as returned by the interface size_t currentPos = 0; // current stream position WMPStream *pStream = pID->pStream; WmpDEMisc *wmiDEMisc = &pID->WMP.wmiDEMisc; BYTE *pbProfile = NULL; try { // save current position error_code = pStream->GetPos(pStream, ¤tPos); JXR_CHECK(error_code); // ICC profile if(0 != wmiDEMisc->uColorProfileByteCount) { unsigned cbByteCount = wmiDEMisc->uColorProfileByteCount; unsigned uOffset = wmiDEMisc->uColorProfileOffset; error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); FreeImage_CreateICCProfile(dib, pbProfile, cbByteCount); } // XMP metadata if(0 != wmiDEMisc->uXMPMetadataByteCount) { unsigned cbByteCount = wmiDEMisc->uXMPMetadataByteCount; unsigned uOffset = wmiDEMisc->uXMPMetadataOffset; error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // store the tag as XMP FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagLength(tag, cbByteCount); FreeImage_SetTagCount(tag, cbByteCount); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, pbProfile); FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); FreeImage_DeleteTag(tag); } } // IPTC metadata if(0 != wmiDEMisc->uIPTCNAAMetadataByteCount) { unsigned cbByteCount = wmiDEMisc->uIPTCNAAMetadataByteCount; unsigned uOffset = wmiDEMisc->uIPTCNAAMetadataOffset; error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // decode the IPTC profile read_iptc_profile(dib, pbProfile, cbByteCount); } // Exif metadata if(0 != wmiDEMisc->uEXIFMetadataByteCount) { unsigned cbByteCount = wmiDEMisc->uEXIFMetadataByteCount; unsigned uOffset = wmiDEMisc->uEXIFMetadataOffset; error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // decode the Exif profile jpegxr_read_exif_profile(dib, pbProfile, cbByteCount, uOffset); } // Exif-GPS metadata if(0 != wmiDEMisc->uGPSInfoMetadataByteCount) { unsigned cbByteCount = wmiDEMisc->uGPSInfoMetadataByteCount; unsigned uOffset = wmiDEMisc->uGPSInfoMetadataOffset; error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // decode the Exif-GPS profile jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount, uOffset); } // free profile buffer free(pbProfile); // restore initial position error_code = pID->pStream->SetPos(pID->pStream, currentPos); JXR_CHECK(error_code); // as a LAST STEP, read descriptive metadata // these metadata overwrite possible identical Exif-TIFF metadata // that could have been read inside the Exif IFD return ReadDescriptiveMetadata(pID, dib); } catch(...) { // free profile buffer free(pbProfile); if(currentPos) { // restore initial position pStream->SetPos(pStream, currentPos); } return error_code; } }
/** Read a single Exif tag @param tif TIFF handle @param tag_id TIFF Tag ID @param dib Image being read @param md_model Metadata model where to store the tag @return Returns TRUE if successful, returns FALSE otherwise */ static BOOL tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) { uint32 value_count = 0; int mem_alloc = 0; void *raw_data = NULL; if(tag_id == TIFFTAG_EXIFIFD) { // Exif IFD offset - skip this tag // md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model return TRUE; } if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) { // Exif GPS IFD offset - skip this tag // should be processed in another way ... return TRUE; } TagLib& tagLib = TagLib::instance(); // get the tag key - use NULL to avoid reading GeoTIFF tags const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL); if(key == NULL) { return TRUE; } const TIFFField *fip = TIFFFieldWithTag(tif, tag_id); if(fip == NULL) { return TRUE; } if(TIFFFieldPassCount(fip)) { // a count value is required for 'TIFFGetField' if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) { // a count is required, it will be of type uint16 uint16 value_count16 = 0; if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) { // stop, ignore error return TRUE; } value_count = value_count16; } else { // a count is required, it will be of type uint32 uint32 value_count32 = 0; if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) { // stop, ignore error return TRUE; } value_count = value_count32; } } else { // determine count if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) { value_count = 1; } else if (TIFFFieldReadCount(fip) == TIFF_SPP) { uint16 spp; TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); value_count = spp; } else { value_count = TIFFFieldReadCount(fip); } // access fields as pointers to data // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) if(TIFFFieldTag(fip) == TIFFTAG_TRANSFERFUNCTION) { // reading this tag cause a bug probably located somewhere inside libtiff return TRUE; } if ((TIFFFieldDataType(fip) == TIFF_ASCII || TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2 || TIFFFieldReadCount(fip) == TIFF_SPP || value_count > 1) && TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER && TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS && TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING && TIFFFieldTag(fip) != TIFFTAG_DOTRANGE && TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - && TIFFFieldTag(fip) != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value ) { if(TIFFGetField(tif, tag_id, &raw_data) != 1) { // stop, ignore error return TRUE; } } else { int value_size = 0; // access fields as values // Note: // For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals. { TIFFDataType tag_type = TIFFFieldDataType(fip); switch(tag_type) { case TIFF_RATIONAL: case TIFF_SRATIONAL: value_size = 4; break; default: value_size = TIFFDataWidth(tag_type); break; } } raw_data = _TIFFmalloc(value_size * value_count); mem_alloc = 1; int ok = FALSE; // ### if value_count > 1, tag is PAGENUMBER or HALFTONEHINTS or YCBCRSUBSAMPLING or DOTRANGE, // all off which are value_count == 2 (see tif_dirinfo.c) switch(value_count) { case 1: ok = TIFFGetField(tif, tag_id, raw_data); break; case 2: ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1); break; /* # we might need more in the future: case 3: ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2); break; */ default: FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip)); break; } if(ok != 1) { _TIFFfree(raw_data); return TRUE; } } } // build FreeImage tag from Tiff Tag data we collected FITAG *fitag = FreeImage_CreateTag(); if(!fitag) { if(mem_alloc) { _TIFFfree(raw_data); } return FALSE; } FreeImage_SetTagID(fitag, (WORD)tag_id); FreeImage_SetTagKey(fitag, key); switch(TIFFFieldDataType(fip)) { case TIFF_BYTE: FreeImage_SetTagType(fitag, FIDT_BYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_UNDEFINED: FreeImage_SetTagType(fitag, FIDT_UNDEFINED); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SBYTE: FreeImage_SetTagType(fitag, FIDT_SBYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SHORT: FreeImage_SetTagType(fitag, FIDT_SHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SSHORT: FreeImage_SetTagType(fitag, FIDT_SSHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG: FreeImage_SetTagType(fitag, FIDT_LONG); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD: FreeImage_SetTagType(fitag, FIDT_IFD); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG: FreeImage_SetTagType(fitag, FIDT_SLONG); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_RATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_SRATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_FLOAT: FreeImage_SetTagType(fitag, FIDT_FLOAT); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_DOUBLE: FreeImage_SetTagType(fitag, FIDT_DOUBLE); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG8: // BigTIFF 64-bit unsigned integer FreeImage_SetTagType(fitag, FIDT_LONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset) FreeImage_SetTagType(fitag, FIDT_IFD8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG8: // BigTIFF 64-bit signed integer FreeImage_SetTagType(fitag, FIDT_SLONG8); FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_ASCII: default: { size_t length = 0; if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) { // when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE), // tiff_read_exif_tag function gives length of 1 so all strings are truncated ... // ... try to avoid this by using an explicit calculation for 'length' length = strlen((char*)raw_data) + 1; } else { // remember that raw_data = _TIFFmalloc(value_size * value_count); const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) ); length = value_size * value_count; } FreeImage_SetTagType(fitag, FIDT_ASCII); FreeImage_SetTagLength(fitag, (DWORD)length); FreeImage_SetTagCount(fitag, (DWORD)length); FreeImage_SetTagValue(fitag, raw_data); } break; } const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id); if(description) { FreeImage_SetTagDescription(fitag, description); } // store the tag FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag); // destroy the tag FreeImage_DeleteTag(fitag); if(mem_alloc) { _TIFFfree(raw_data); } return TRUE; }
/** Process a Canon maker note tag. A single Canon tag may contain many other tags within. */ static void processCanonMakerNoteTag(FIBITMAP *dib, FITAG *tag) { char defaultKey[16]; DWORD startIndex = 0; TagLib& s = TagLib::instance(); WORD tag_id = FreeImage_GetTagID(tag); if((tag_id == TAG_CANON_CAMERA_STATE_1) || (tag_id == TAG_CANON_CAMERA_STATE_2) || (tag_id == TAG_CANON_CAMERA_STATE_4)) { // this single tag has multiple values within int subTagTypeBase = 0; switch(tag_id) { case TAG_CANON_CAMERA_STATE_1: subTagTypeBase = 0xC100; startIndex = 1; break; case TAG_CANON_CAMERA_STATE_2: subTagTypeBase = 0xC200; startIndex = 0; break; case TAG_CANON_CAMERA_STATE_4: subTagTypeBase = 0xC400; startIndex = 2; break; } WORD *pvalue = (WORD*)FreeImage_GetTagValue(tag); // we intentionally skip the first array member for (DWORD i = startIndex; i < FreeImage_GetTagCount(tag); i++) { // create a tag FITAG *canonTag = FreeImage_CreateTag(); if(!canonTag) return; 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); } } else { // 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); } } }
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++) { ttag_t tag = TIFFGetTagListEntry(tif, i); const TIFFFieldInfo *fip; uint32 value_count; int mem_alloc = 0; void *raw_data; if(tag == TIFFTAG_EXIFIFD) continue; // get the tag key - use NULL to avoid reading GeoTIFF tags const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL); if(key == NULL) continue; fip = TIFFFieldWithTag(tif, tag); if(fip == NULL) continue; if(fip->field_passcount) { if (fip->field_readcount != TIFF_VARIABLE2) { // assume TIFF_VARIABLE uint16 value_count16; if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) continue; value_count = value_count16; } else { if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) continue; } } else { if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) { value_count = 1; } else if (fip->field_readcount == TIFF_SPP) { value_count = td->td_samplesperpixel; } else { value_count = fip->field_readcount; } if (fip->field_type == TIFF_ASCII || fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2 || fip->field_readcount == TIFF_SPP || value_count > 1) { if(TIFFGetField(tif, tag, &raw_data) != 1) continue; } else { raw_data = _TIFFmalloc(_TIFFDataSize(fip->field_type) * value_count); mem_alloc = 1; if(TIFFGetField(tif, tag, raw_data) != 1) { _TIFFfree(raw_data); continue; } } } // create a tag FITAG *fitag = FreeImage_CreateTag(); if(!fitag) { if(mem_alloc) _TIFFfree(raw_data); return FALSE; } FreeImage_SetTagID(fitag, (WORD)tag); FreeImage_SetTagKey(fitag, key); switch(fip->field_type) { case TIFF_BYTE: FreeImage_SetTagType(fitag, FIDT_BYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_UNDEFINED: FreeImage_SetTagType(fitag, FIDT_UNDEFINED); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SBYTE: FreeImage_SetTagType(fitag, FIDT_SBYTE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SHORT: FreeImage_SetTagType(fitag, FIDT_SHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SSHORT: FreeImage_SetTagType(fitag, FIDT_SSHORT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG: FreeImage_SetTagType(fitag, FIDT_LONG); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD: FreeImage_SetTagType(fitag, FIDT_IFD); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG: FreeImage_SetTagType(fitag, FIDT_SLONG); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_RATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_SRATIONAL: { // LibTIFF converts rational to floats : reconvert floats to rationals LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG)); for(uint32 i = 0; i < value_count; i++) { float *fv = (float*)raw_data; FIRational rational(fv[i]); rvalue[2*i] = rational.getNumerator(); rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); } break; case TIFF_FLOAT: FreeImage_SetTagType(fitag, FIDT_FLOAT); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_DOUBLE: FreeImage_SetTagType(fitag, FIDT_DOUBLE); FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; default: { size_t length = strlen((char*)raw_data) + 1; FreeImage_SetTagType(fitag, FIDT_ASCII); FreeImage_SetTagLength(fitag, (DWORD)length); FreeImage_SetTagCount(fitag, (DWORD)length); FreeImage_SetTagValue(fitag, raw_data); } break; } const char *description = tagLib.getTagDescription(md_model, (WORD)tag); if(description) { FreeImage_SetTagDescription(fitag, description); } // store the tag FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag); // destroy the tag FreeImage_DeleteTag(fitag); if(mem_alloc) _TIFFfree(raw_data); } return TRUE; }
/** Read and decode IPTC binary data */ BOOL read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { char defaultKey[16]; size_t length = datalen; BYTE *profile = (BYTE*)dataptr; std::string Keywords; std::string SupplementalCategory; WORD tag_id; // create a tag FITAG *tag = FreeImage_CreateTag(); TagLib& tag_lib = TagLib::instance(); // find start of the BIM portion of the binary data size_t offset = 0; while(offset < length - 1) { if((profile[offset] == 0x1C) && (profile[offset+1] == 0x02)) break; offset++; } // for each tag while (offset < length) { // identifies start of a tag if (profile[offset] != 0x1c) { break; } // we need at least five bytes left to read a tag if ((offset + 5) >= length) { break; } offset++; int directoryType = profile[offset++]; int tagType = profile[offset++];; int tagByteCount = ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF); offset += 2; if ((offset + tagByteCount) > length) { // data for tag extends beyond end of iptc segment break; } // process the tag tag_id = (WORD)(tagType | (directoryType << 8)); FreeImage_SetTagID(tag, tag_id); FreeImage_SetTagLength(tag, tagByteCount); // allocate a buffer to store the tag value BYTE *iptc_value = (BYTE*)malloc((tagByteCount + 1) * sizeof(BYTE)); memset(iptc_value, 0, (tagByteCount + 1) * sizeof(BYTE)); // get the tag value switch (tag_id) { case TAG_RECORD_VERSION: { // short FreeImage_SetTagType(tag, FIDT_SSHORT); FreeImage_SetTagCount(tag, 1); short *pvalue = (short*)&iptc_value[0]; *pvalue = (short)((profile[offset] << 8) | profile[offset + 1]); FreeImage_SetTagValue(tag, pvalue); break; } case TAG_RELEASE_DATE: case TAG_DATE_CREATED: // Date object case TAG_RELEASE_TIME: case TAG_TIME_CREATED: // time default: { // string FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagCount(tag, tagByteCount); for(int i = 0; i < tagByteCount; i++) { iptc_value[i] = profile[offset + i]; } iptc_value[tagByteCount] = '\0'; FreeImage_SetTagValue(tag, (char*)&iptc_value[0]); break; } } if(tag_id == TAG_SUPPLEMENTAL_CATEGORIES) { // concatenate the categories if(SupplementalCategory.length() == 0) { SupplementalCategory.append((char*)iptc_value); } else { SupplementalCategory.append(IPTC_DELIMITER); SupplementalCategory.append((char*)iptc_value); } } else if(tag_id == TAG_KEYWORDS) { // concatenate the keywords if(Keywords.length() == 0) { Keywords.append((char*)iptc_value); } else { Keywords.append(IPTC_DELIMITER); Keywords.append((char*)iptc_value); } } else { // get the tag key and description const char *key = tag_lib.getTagFieldName(TagLib::IPTC, tag_id, defaultKey); FreeImage_SetTagKey(tag, key); const char *description = tag_lib.getTagDescription(TagLib::IPTC, tag_id); FreeImage_SetTagDescription(tag, description); // store the tag if(key) { FreeImage_SetMetadata(FIMD_IPTC, dib, key, tag); } } free(iptc_value); // next tag offset += tagByteCount; } // store the 'keywords' tag if(Keywords.length()) { FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagID(tag, TAG_KEYWORDS); FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_KEYWORDS, defaultKey)); FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_KEYWORDS)); FreeImage_SetTagLength(tag, (DWORD)Keywords.length()); FreeImage_SetTagCount(tag, (DWORD)Keywords.length()); FreeImage_SetTagValue(tag, (char*)Keywords.c_str()); FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag); } // store the 'supplemental category' tag if(SupplementalCategory.length()) { FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagID(tag, TAG_SUPPLEMENTAL_CATEGORIES); FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES, defaultKey)); FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES)); FreeImage_SetTagLength(tag, (DWORD)SupplementalCategory.length()); FreeImage_SetTagCount(tag, (DWORD)SupplementalCategory.length()); FreeImage_SetTagValue(tag, (char*)SupplementalCategory.c_str()); FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag); } // delete the tag FreeImage_DeleteTag(tag); return TRUE; }
/** Convert a DPKPROPVARIANT to a FITAG, then store the tag as FIMD_EXIF_MAIN @see ReadDescriptiveMetadata */ static BOOL ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) { DWORD dwSize; if(varSrc.vt == DPKVT_EMPTY) { return FALSE; } // get the tag key TagLib& s = TagLib::instance(); const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL); if(!key) { return FALSE; } // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { // set tag ID FreeImage_SetTagID(tag, tag_id); // set tag type, count, length and value switch (varSrc.vt) { case DPKVT_LPSTR: FreeImage_SetTagType(tag, FIDT_ASCII); dwSize = (DWORD)strlen(varSrc.VT.pszVal) + 1; FreeImage_SetTagCount(tag, dwSize); FreeImage_SetTagLength(tag, dwSize); FreeImage_SetTagValue(tag, varSrc.VT.pszVal); break; case DPKVT_LPWSTR: FreeImage_SetTagType(tag, FIDT_UNDEFINED); dwSize = (DWORD)(sizeof(U16) * (wcslen((wchar_t *) varSrc.VT.pwszVal) + 1)); // +1 for NULL term FreeImage_SetTagCount(tag, dwSize); FreeImage_SetTagLength(tag, dwSize); FreeImage_SetTagValue(tag, varSrc.VT.pwszVal); break; case DPKVT_UI2: FreeImage_SetTagType(tag, FIDT_SHORT); FreeImage_SetTagCount(tag, 1); FreeImage_SetTagLength(tag, 2); FreeImage_SetTagValue(tag, &varSrc.VT.uiVal); break; case DPKVT_UI4: FreeImage_SetTagType(tag, FIDT_LONG); FreeImage_SetTagCount(tag, 1); FreeImage_SetTagLength(tag, 4); FreeImage_SetTagValue(tag, &varSrc.VT.ulVal); break; default: assert(FALSE); // This case is not handled break; } // get the tag desctiption const char *description = s.getTagDescription(TagLib::EXIF_MAIN, tag_id); FreeImage_SetTagDescription(tag, description); // store the tag FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, key, tag); FreeImage_DeleteTag(tag); } return TRUE; }