void ofxGifDecoder::createGifFile(FIBITMAP * bmp, int _nPages){ FITAG *tag; int logicalWidth, logicalHeight; if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "LogicalWidth", &tag)) { logicalWidth = *(unsigned short *)FreeImage_GetTagValue(tag); } if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "LogicalHeight", &tag)) { logicalHeight = *(unsigned short *)FreeImage_GetTagValue(tag); } if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "GlobalPalette", &tag) ) { globalPaletteSize = FreeImage_GetTagCount(tag); if( globalPaletteSize >= 2 ) { globalPalette = (RGBQUAD *)FreeImage_GetTagValue(tag); for (int i = 0 ; i < globalPaletteSize; i++) { ofColor c; c.set(globalPalette[i].rgbRed, globalPalette[i].rgbGreen, globalPalette[i].rgbBlue); palette.push_back(c); } } } gifFile.setup(logicalWidth, logicalHeight, palette, _nPages); RGBQUAD bgColor; if(FreeImage_GetBackgroundColor(bmp, &bgColor)){ gifFile.setBackgroundColor(ofColor(bgColor.rgbRed, bgColor.rgbGreen, bgColor.rgbBlue, 0)); } }
void tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { char defaultKey[16]; if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) { return; } size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); TagLib& tag_lib = TagLib::instance(); for(unsigned i = 0; i < tag_size; i++) { const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i]; FITAG *tag = NULL; const char *key = tag_lib.getTagFieldName(TagLib::GEOTIFF, (WORD)fieldInfo->field_tag, defaultKey); if(FreeImage_GetMetadata(FIMD_GEOTIFF, dib, key, &tag)) { if(FreeImage_GetTagType(tag) == FIDT_ASCII) { TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagValue(tag)); } else { TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); } } } }
static BOOL WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; FIMETADATA *mdhandle = NULL; BOOL bResult = TRUE; png_text text_metadata; int num_text = 0; // set the 'Comments' metadata as iTXt chuncks mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag); if(mdhandle) { do { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); } while(FreeImage_FindNextMetadata(mdhandle, &tag)); FreeImage_FindCloseMetadata(mdhandle); bResult &= TRUE; } // set the 'XMP' metadata as iTXt chuncks tag = NULL; FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag); if(tag && FreeImage_GetTagLength(tag)) { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = g_png_xmp_keyword; // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); bResult &= TRUE; } return bResult; }
/** Write all known exif tags @param tif TIFF handle @param md_model Metadata model from where to load the tags @param dib Image being written @return Returns TRUE if successful, returns FALSE otherwise */ BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { char defaultKey[16]; // only EXIF_MAIN so far if(md_model != TagLib::EXIF_MAIN) { return FALSE; } if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib) == 0) { return FALSE; } TagLib& tag_lib = TagLib::instance(); for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFField *fld = tif->tif_fields[fi]; const uint32 tag_id = TIFFFieldTag(fld); if(skip_write_field(tif, tag_id)) { // skip tags that are already handled by the LibTIFF writing process continue; } FITAG *tag = NULL; // get the tag key const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey); if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); TIFFDataType tif_tag_type = TIFFFieldDataType(fld); // check for identical formats // (enum value are the sames between FREE_IMAGE_MDTYPE and TIFFDataType types) if((int)tif_tag_type != (int)tag_type) { // skip tag or _TIFFmemcpy will fail continue; } // type of storage may differ (e.g. rationnal array vs float array type) if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { // skip tag or _TIFFmemcpy will fail continue; } if(tag_type == FIDT_ASCII) { TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag)); } else { TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); } } } return TRUE; }
static BOOL FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag) { if( FreeImage_GetMetadata(model, dib, key, tag) ) { if( FreeImage_GetTagType(*tag) == type ) { return TRUE; } } return FALSE; }
static void rotate_exif(FIBITMAP **dib) { // check for Exif rotation if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, *dib)) { FIBITMAP *rotated = NULL; // process Exif rotation FITAG *tag = NULL; FreeImage_GetMetadata(FIMD_EXIF_MAIN, *dib, "Orientation", &tag); if(tag != NULL) { if(FreeImage_GetTagID(tag) == TAG_ORIENTATION) { unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag)); switch (orientation) { case 1: // "top, left side" => 0° break; case 2: // "top, right side" => flip left-right FreeImage_FlipHorizontal(*dib); break; case 3: // "bottom, right side"; => -180° rotated = FreeImage_Rotate(*dib, 180); FreeImage_Unload(*dib); *dib = rotated; break; case 4: // "bottom, left side" => flip up-down FreeImage_FlipVertical(*dib); break; case 5: // "left side, top" => +90° + flip up-down rotated = FreeImage_Rotate(*dib, 90); FreeImage_Unload(*dib); *dib = rotated; FreeImage_FlipVertical(*dib); break; case 6: // "right side, top" => -90° rotated = FreeImage_Rotate(*dib, -90); FreeImage_Unload(*dib); *dib = rotated; break; case 7: // "right side, bottom" => -90° + flip up-down rotated = FreeImage_Rotate(*dib, -90); FreeImage_Unload(*dib); *dib = rotated; FreeImage_FlipVertical(*dib); break; case 8: // "left side, bottom" => +90° rotated = FreeImage_Rotate(*dib, 90); FreeImage_Unload(*dib); *dib = rotated; break; default: break; } } } } }
BOOL fipImage::getMetadata(FREE_IMAGE_MDMODEL model, const char *key, fipTag& tag) const { FITAG *searchedTag = NULL; FreeImage_GetMetadata(model, _dib, key, &searchedTag); if(searchedTag != NULL) { tag = FreeImage_CloneTag(searchedTag); return TRUE; } else { // clear the tag tag = (FITAG*)NULL; } return FALSE; }
int FreeImageGifData::getDelayValue(int index) { if (m_gifHandle) { FIBITMAP* frame = FreeImage_LockPage(m_gifHandle, index); FITAG* delayTag = NULL; if (!FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &delayTag)) { FreeImage_UnlockPage(m_gifHandle, frame, false); return -1; } LONG delayVal = *(LONG*)FreeImage_GetTagValue(delayTag); FreeImage_UnlockPage(m_gifHandle, frame, false); return delayVal / 10; } return -1; }
/** Write JPEG_COM marker (comment) */ static BOOL jpeg_write_comment(j_compress_ptr cinfo, FIBITMAP *dib) { FITAG *tag = NULL; // write user comment as a JPEG_COM marker FreeImage_GetMetadata(FIMD_COMMENTS, dib, "Comment", &tag); if(tag) { const char *tag_value = (char*)FreeImage_GetTagValue(tag); if(NULL != tag_value) { for(long i = 0; i < (long)strlen(tag_value); i+= MAX_BYTES_IN_MARKER) { jpeg_write_marker(cinfo, JPEG_COM, (BYTE*)tag_value + i, MIN((long)strlen(tag_value + i), MAX_BYTES_IN_MARKER)); } return TRUE; } } return FALSE; }
/** Convert a FITAG (coming from FIMD_EXIF_MAIN) to a DPKPROPVARIANT. No allocation is needed here, the function just copy pointers when needed. @see WriteDescriptiveMetadata */ static BOOL WritePropVariant(FIBITMAP *dib, WORD tag_id, DPKPROPVARIANT & varDst) { FITAG *tag = NULL; TagLib& s = TagLib::instance(); // clear output DPKPROPVARIANT varDst.vt = DPKVT_EMPTY; // given the tag id, get the tag key const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL); // then, get the tag info if(!FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { return FALSE; } // set the tag value switch(FreeImage_GetTagType(tag)) { case FIDT_ASCII: varDst.vt = DPKVT_LPSTR; varDst.VT.pszVal = (char*)FreeImage_GetTagValue(tag); break; case FIDT_BYTE: case FIDT_UNDEFINED: varDst.vt = DPKVT_LPWSTR; varDst.VT.pwszVal = (U16*)FreeImage_GetTagValue(tag); break; case FIDT_SHORT: varDst.vt = DPKVT_UI2; varDst.VT.uiVal = *((U16*)FreeImage_GetTagValue(tag)); break; case FIDT_LONG: varDst.vt = DPKVT_UI4; varDst.VT.ulVal = *((U32*)FreeImage_GetTagValue(tag)); break; default: break; } return TRUE; }
/** Write JPEG_APP1 marker (XMP profile) @return Returns TRUE if successful, FALSE otherwise */ static BOOL jpeg_write_xmp_profile(j_compress_ptr cinfo, FIBITMAP *dib) { // marker identifying string for XMP (null terminated) char *xmp_signature = "http://ns.adobe.com/xap/1.0/"; FITAG *tag_xmp = NULL; FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp); if(tag_xmp) { const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_xmp); if(NULL != tag_value) { // XMP signature is 29 bytes long unsigned int xmp_header_size = (unsigned int)strlen(xmp_signature) + 1; DWORD tag_length = FreeImage_GetTagLength(tag_xmp); BYTE *profile = (BYTE*)malloc((tag_length + xmp_header_size) * sizeof(BYTE)); if(profile == NULL) return FALSE; memcpy(profile, xmp_signature, xmp_header_size); for(DWORD i = 0; i < tag_length; i += 65504L) { unsigned length = MIN((long)(tag_length - i), 65504L); memcpy(profile + xmp_header_size, tag_value + i, length); jpeg_write_marker(cinfo, EXIF_MARKER, profile, (length + xmp_header_size)); } free(profile); return TRUE; } } return FALSE; }
static void *bg_new_img(void *data) { /* so we can poll for it */ block_usr1_signal(); struct imv_loader *ldr = data; char path[PATH_MAX] = "-"; pthread_mutex_lock(&ldr->lock); int from_stdin = !strncmp(path, ldr->path, 2); if(!from_stdin) { (void)snprintf(path, PATH_MAX, "%s", ldr->path); } pthread_mutex_unlock(&ldr->lock); FREE_IMAGE_FORMAT fmt; if (from_stdin) { pthread_mutex_lock(&ldr->lock); ldr->fi_buffer = FreeImage_OpenMemory(ldr->buffer, ldr->buffer_size); fmt = FreeImage_GetFileTypeFromMemory(ldr->fi_buffer, 0); pthread_mutex_unlock(&ldr->lock); } else { fmt = FreeImage_GetFileType(path, 0); } if(fmt == FIF_UNKNOWN) { if (from_stdin) { pthread_mutex_lock(&ldr->lock); FreeImage_CloseMemory(ldr->fi_buffer); pthread_mutex_unlock(&ldr->lock); } error_occurred(ldr); return NULL; } int num_frames = 1; FIMULTIBITMAP *mbmp = NULL; FIBITMAP *bmp = NULL; int width, height; int raw_frame_time = 100; /* default to 100 */ if(fmt == FIF_GIF) { if(from_stdin) { pthread_mutex_lock(&ldr->lock); mbmp = FreeImage_LoadMultiBitmapFromMemory(FIF_GIF, ldr->fi_buffer, GIF_LOAD256); pthread_mutex_unlock(&ldr->lock); } else { mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path, /* don't create file */ 0, /* read only */ 1, /* keep in memory */ 1, /* flags */ GIF_LOAD256); } if(!mbmp) { error_occurred(ldr); return NULL; } num_frames = FreeImage_GetPageCount(mbmp); FIBITMAP *frame = FreeImage_LockPage(mbmp, 0); width = FreeImage_GetWidth(frame); height = FreeImage_GetHeight(frame); bmp = FreeImage_ConvertTo32Bits(frame); /* get duration of first frame */ FITAG *tag = NULL; FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); if(FreeImage_GetTagValue(tag)) { raw_frame_time = *(int*)FreeImage_GetTagValue(tag); } FreeImage_UnlockPage(mbmp, frame, 0); } else { /* Future TODO: If we load image line-by-line we could stop loading large * ones before wasting much more time/memory on them. */ int flags = (fmt == FIF_JPEG) ? JPEG_EXIFROTATE : 0; FIBITMAP *image; if(from_stdin) { pthread_mutex_lock(&ldr->lock); image = FreeImage_LoadFromMemory(fmt, ldr->fi_buffer, flags); pthread_mutex_unlock(&ldr->lock); } else { image = FreeImage_Load(fmt, path, flags); } if(!image) { error_occurred(ldr); pthread_mutex_lock(&ldr->lock); FreeImage_CloseMemory(ldr->fi_buffer); ldr->fi_buffer = NULL; pthread_mutex_unlock(&ldr->lock); return NULL; } /* Check for cancellation before we convert pixel format */ if(is_thread_cancelled()) { FreeImage_Unload(image); return NULL; } width = FreeImage_GetWidth(bmp); height = FreeImage_GetHeight(bmp); bmp = FreeImage_ConvertTo32Bits(image); FreeImage_Unload(image); } /* now update the loader */ pthread_mutex_lock(&ldr->lock); /* check for cancellation before finishing */ if(is_thread_cancelled()) { if(mbmp) { FreeImage_CloseMultiBitmap(mbmp, 0); } if(bmp) { FreeImage_Unload(bmp); } pthread_mutex_unlock(&ldr->lock); return NULL; } if(ldr->mbmp) { FreeImage_CloseMultiBitmap(ldr->mbmp, 0); } if(ldr->bmp) { FreeImage_Unload(ldr->bmp); } ldr->mbmp = mbmp; ldr->bmp = bmp; if(ldr->out_bmp) { FreeImage_Unload(ldr->out_bmp); } ldr->out_bmp = FreeImage_Clone(bmp); ldr->out_is_new_image = 1; ldr->width = width; ldr->height = height; ldr->cur_frame = 0; ldr->next_frame = 1; ldr->num_frames = num_frames; ldr->frame_time = (double)raw_frame_time * 0.0001; pthread_mutex_unlock(&ldr->lock); return NULL; }
static BOOL WriteMetadata(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; FIMETADATA *mdhandle = NULL; BOOL bResult = TRUE; png_text text_metadata; png_time mod_time; // set the 'Comments' metadata as iTXt chuncks mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag); if(mdhandle) { do { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); } while(FreeImage_FindNextMetadata(mdhandle, &tag)); FreeImage_FindCloseMetadata(mdhandle); bResult &= TRUE; } // set the 'XMP' metadata as iTXt chuncks tag = NULL; FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag); if(tag && FreeImage_GetTagLength(tag)) { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)g_png_xmp_keyword; // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); bResult &= TRUE; } // set the Exif-TIFF 'DateTime' metadata as a tIME chunk tag = NULL; FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "DateTime", &tag); if(tag && FreeImage_GetTagLength(tag)) { int year, month, day, hour, minute, second; const char *value = (char*)FreeImage_GetTagValue(tag); if(sscanf(value, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) { mod_time.year = year; mod_time.month = month; mod_time.day = day; mod_time.hour = hour; mod_time.minute = minute; mod_time.second = second; png_set_tIME (png_ptr, info_ptr, &mod_time); } } return bResult; }
FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib) { RGBQUAD *rgb; const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); // special bitmap type if(image_type != FIT_BITMAP) { switch(image_type) { case FIT_UINT16: { // 16-bit greyscale TIF can be either FIC_MINISBLACK (the most common case) or FIC_MINISWHITE // you can check this using EXIF_MAIN metadata FITAG *photometricTag = NULL; if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "PhotometricInterpretation", &photometricTag)) { const short *value = (short*)FreeImage_GetTagValue(photometricTag); // PHOTOMETRIC_MINISWHITE = 0 => min value is white // PHOTOMETRIC_MINISBLACK = 1 => min value is black return (*value == 0) ? FIC_MINISWHITE : FIC_MINISBLACK; } return FIC_MINISBLACK; } break; case FIT_RGB16: case FIT_RGBF: return FIC_RGB; case FIT_RGBA16: case FIT_RGBAF: return FIC_RGBALPHA; } return FIC_MINISBLACK; } // standard image type switch (FreeImage_GetBPP(dib)) { case 1: { rgb = FreeImage_GetPalette(dib); if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { rgb++; if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) { return FIC_MINISBLACK; } } if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) { rgb++; if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { return FIC_MINISWHITE; } } return FIC_PALETTE; } case 4: case 8: // Check if the DIB has a color or a greyscale palette { int ncolors = FreeImage_GetColorsUsed(dib); int minisblack = 1; rgb = FreeImage_GetPalette(dib); for (int i = 0; i < ncolors; i++) { if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) { return FIC_PALETTE; } // The DIB has a color palette if the greyscale isn't a linear ramp // Take care of reversed grey images if (rgb->rgbRed != i) { if ((ncolors-i-1) != rgb->rgbRed) { return FIC_PALETTE; } else { minisblack = 0; } } rgb++; } return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE; } case 16: case 24: return FIC_RGB; case 32: { if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) { return FIC_CMYK; } if( FreeImage_HasPixels(dib) ) { // check for fully opaque alpha layer for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { if (rgb[x].rgbReserved != 0xFF) { return FIC_RGBALPHA; } } } return FIC_RGB; } return FIC_RGBALPHA; } default : return FIC_MINISBLACK; } }
void ofxGifDecoder::processFrame(FIBITMAP * bmp, int _frameNum){ FITAG *tag; ofPixels pix; unsigned int frameLeft, frameTop; float frameDuration; GifFrameDisposal disposal_method = GIF_DISPOSAL_BACKGROUND; if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "FrameLeft", &tag)) { frameLeft = *(unsigned short *)FreeImage_GetTagValue(tag); } if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "FrameTop", &tag)) { frameTop = *(unsigned short *)FreeImage_GetTagValue(tag); } if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "FrameTime", &tag)) { long frameTime = *(long *)FreeImage_GetTagValue(tag);// centiseconds 1/100 sec frameDuration =(float)frameTime/1000.f; } if( FreeImage_GetMetadata(FIMD_ANIMATION, bmp, "DisposalMethod", &tag)) { disposal_method = (GifFrameDisposal) *(unsigned char *)FreeImage_GetTagValue(tag); } // we do this for drawing. eventually we should be able to draw 8 bits? at least to retain the data // if(FreeImage_GetBPP(bmp) == 8) { // // maybe we should only do this when asked for rendering? // bmp = FreeImage_ConvertTo24Bits(bmp); // } FIBITMAP* bmpConverted = NULL; if(FreeImage_GetColorType(bmp) == FIC_PALETTE || FreeImage_GetBPP(bmp) < 8) { if(FreeImage_IsTransparent(bmp)) { bmpConverted = FreeImage_ConvertTo32Bits(bmp); } else { bmpConverted = FreeImage_ConvertTo24Bits(bmp); } bmp = bmpConverted; } unsigned int width = FreeImage_GetWidth(bmp); unsigned int height = FreeImage_GetHeight(bmp); unsigned int bpp = FreeImage_GetBPP(bmp); // changed this bc we're not using PixelType template anywhere else... unsigned int channels = (bpp / sizeof( unsigned char )) / 8; unsigned int pitch = FreeImage_GetPitch(bmp); // ofPixels are top left, FIBITMAP is bottom left FreeImage_FlipVertical(bmp); unsigned char * bmpBits = FreeImage_GetBits(bmp); if(bmpBits != NULL) { pix.setFromAlignedPixels(bmpBits, width, height, channels, pitch); #ifdef TARGET_LITTLE_ENDIAN pix.swapRgb(); #endif gifFile.addFrame(pix, frameLeft, frameTop, disposal_method, frameDuration); } else { ofLogError() << "ofImage::putBmpIntoPixels() unable to set ofPixels from FIBITMAP"; } }
/** 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; } }
/** Process a maker note IFD offset Returns the offset and the metadata model for this tag */ static void processMakerNote(FIBITMAP *dib, char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { FITAG *tagMake = NULL; *subdirOffset = 0; *md_model = TagLib::UNKNOWN; // Determine the camera model and makernote format // WARNING: note that Maker may be NULL sometimes so check its value before using it // (NULL pointer checking is done by FreeImage_strnicmp) FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "Make", &tagMake); const char *Maker = (char*)FreeImage_GetTagValue(tagMake); if((strncmp("OLYMP\x00\x01", pval, 7) == 0) || (strncmp("OLYMP\x00\x02", pval, 7) == 0) || (strncmp("EPSON", pval, 5) == 0) || (strncmp("AGFA", pval, 4) == 0)) { // Olympus Type 1 Makernote // Epson and Agfa use Olympus maker note standard, // see: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ *md_model = TagLib::EXIF_MAKERNOTE_OLYMPUSTYPE1; *subdirOffset = 8; } else if(strncmp("OLYMPUS\x00\x49\x49\x03\x00", pval, 12) == 0) { // Olympus Type 2 Makernote // !!! NOT YET SUPPORTED !!! *subdirOffset = 0; *md_model = TagLib::UNKNOWN; } else if(strncmp("Nikon", pval, 5) == 0) { /* There are two scenarios here: * Type 1: * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ * Type 3: * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 */ if (pval[6] == 1) { // Nikon type 1 Makernote *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE1; *subdirOffset = 8; } else if (pval[6] == 2) { // Nikon type 3 Makernote *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE3; *subdirOffset = 18; } else { // Unsupported makernote data ignored *md_model = TagLib::UNKNOWN; } } else if(Maker && (FreeImage_strnicmp("NIKON", Maker, 5) == 0)) { // Nikon type 2 Makernote *md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE2; *subdirOffset = 0; } else if(Maker && (FreeImage_strnicmp("Canon", Maker, 5) == 0)) { // Canon Makernote *md_model = TagLib::EXIF_MAKERNOTE_CANON; *subdirOffset = 0; } else if(Maker && (FreeImage_strnicmp("Casio", Maker, 5) == 0)) { // Casio Makernote if(strncmp("QVC\x00\x00\x00", pval, 6) == 0) { // Casio Type 2 Makernote *md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE2; *subdirOffset = 6; } else { // Casio Type 1 Makernote *md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE1; *subdirOffset = 0; } } else if ((strncmp("FUJIFILM", pval, 8) == 0) || (Maker && (FreeImage_strnicmp("Fujifilm", Maker, 8) == 0))) { // Fujifile Makernote // Fujifilm's Makernote always use Intel order altough the Exif section maybe in Intel order or in Motorola order. // If msb_order == TRUE, the Makernote won't be read: // the value of ifdStart will be 0x0c000000 instead of 0x0000000c and the MakerNote section will be discarded later // in jpeg_read_exif_dir because the IFD is too high *md_model = TagLib::EXIF_MAKERNOTE_FUJIFILM; DWORD ifdStart = (DWORD) ReadUint32(msb_order, pval + 8); *subdirOffset = ifdStart; } else if(memcmp("KYOCERA \x00\x00\x00", pval, 22) == 0) { *md_model = TagLib::EXIF_MAKERNOTE_KYOCERA; *subdirOffset = 22; } else if(Maker && (FreeImage_strnicmp("Minolta", Maker, 7) == 0)) { // Minolta maker note *md_model = TagLib::EXIF_MAKERNOTE_MINOLTA; *subdirOffset = 0; } else if(memcmp("Panasonic\x00\x00\x00", pval, 12) == 0) { // Panasonic maker note *md_model = TagLib::EXIF_MAKERNOTE_PANASONIC; *subdirOffset = 12; } else if(Maker && ((FreeImage_strnicmp("Pentax", Maker, 6) == 0) || (FreeImage_strnicmp("Asahi", Maker, 5) == 0))) { // Pentax maker note if(strncmp("AOC\x00", pval, 4) == 0) { // Type 2 Pentax Makernote *md_model = TagLib::EXIF_MAKERNOTE_PENTAX; *subdirOffset = 6; } else { // Type 1 Pentax Makernote *md_model = TagLib::EXIF_MAKERNOTE_ASAHI; *subdirOffset = 0; } } else if((strncmp("SONY CAM", pval, 8) == 0) || (strncmp("SONY DSC", pval, 8) == 0)) { *md_model = TagLib::EXIF_MAKERNOTE_SONY; *subdirOffset = 12; } }
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; } }
static void *bg_next_frame(void *data) { struct imv_loader *ldr = data; pthread_mutex_lock(&ldr->lock); int num_frames = ldr->num_frames; if(num_frames < 2) { pthread_mutex_unlock(&ldr->lock); return NULL; } FITAG *tag = NULL; char disposal_method = 0; int frame_time = 0; short top = 0; short left = 0; ldr->cur_frame = ldr->next_frame; ldr->next_frame = (ldr->cur_frame + 1) % ldr->num_frames; FIBITMAP *frame = FreeImage_LockPage(ldr->mbmp, ldr->cur_frame); FIBITMAP *frame32 = FreeImage_ConvertTo32Bits(frame); /* First frame is always going to use the raw frame */ if(ldr->cur_frame > 0) { FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag); if(FreeImage_GetTagValue(tag)) { disposal_method = *(char*)FreeImage_GetTagValue(tag); } } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag); if(FreeImage_GetTagValue(tag)) { left = *(short*)FreeImage_GetTagValue(tag); } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &tag); if(FreeImage_GetTagValue(tag)) { top = *(short*)FreeImage_GetTagValue(tag); } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); if(FreeImage_GetTagValue(tag)) { frame_time = *(int*)FreeImage_GetTagValue(tag); } /* some gifs don't provide a frame time at all */ if(frame_time == 0) { frame_time = 100; } ldr->frame_time += frame_time * 0.001; FreeImage_UnlockPage(ldr->mbmp, frame, 0); /* If this frame is inset, we need to expand it for compositing */ if(ldr->width != (int)FreeImage_GetWidth(frame32) || ldr->height != (int)FreeImage_GetHeight(frame32)) { FIBITMAP *expanded = FreeImage_Allocate(ldr->width, ldr->height, 32, 0,0,0); FreeImage_Paste(expanded, frame32, left, top, 255); FreeImage_Unload(frame32); frame32 = expanded; } switch(disposal_method) { case 0: /* nothing specified, fall through to compositing */ case 1: /* composite over previous frame */ if(ldr->bmp && ldr->cur_frame > 0) { FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(ldr->bmp); FreeImage_Unload(ldr->bmp); FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame); FreeImage_Unload(bg_frame); FreeImage_Unload(frame32); ldr->bmp = comp; } else { /* No previous frame, just render directly */ if(ldr->bmp) { FreeImage_Unload(ldr->bmp); } ldr->bmp = frame32; } break; case 2: /* TODO - set to background, composite over that */ if(ldr->bmp) { FreeImage_Unload(ldr->bmp); } ldr->bmp = frame32; break; case 3: /* TODO - restore to previous content */ if(ldr->bmp) { FreeImage_Unload(ldr->bmp); } ldr->bmp = frame32; break; } if(ldr->out_bmp) { FreeImage_Unload(ldr->out_bmp); } ldr->out_bmp = FreeImage_Clone(ldr->bmp); pthread_mutex_unlock(&ldr->lock); return NULL; }