Пример #1
0
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, &params)) {
				// 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)
}
Пример #2
0
/**
Write a metadata model as a TIF IFD to a FIMEMORY handle.
The entries in the TIF IFD are sorted in ascending order by tag id.	
The last entry is written as 0 (4 bytes) which means no more IFD to follow. 
Supported metadata models are
<ul>
<li>FIMD_EXIF_MAIN
<li>FIMD_EXIF_EXIF
<li>FIMD_EXIF_GPS
<li>FIMD_EXIF_INTEROP
</ul>
The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset)

@param dib Input FIBITMAP
@param md_model Metadata model to write
@param hmem Memory handle
@return Returns TRUE if successful, FALSE otherwise
@see tiff_get_ifd_profile
*/
static BOOL
tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) {
	FITAG *tag = NULL;
	FIMETADATA *mdhandle = NULL;
	std::vector<FITAG*> vTagList;
	TagLib::MDMODEL internal_md_model;

	DWORD ifd_offset = 0;	// WORD-aligned IFD value offset

	const BYTE empty_byte = 0;

	// start of the file
	const long start_of_file = FreeImage_TellMemory(hmem);

	// get the metadata count
	unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib);
	if(metadata_count == 0) {
		return FALSE;
	}

	TagLib& s = TagLib::instance();

	// check for supported metadata models
	switch(md_model) {
		case FIMD_EXIF_MAIN:
			internal_md_model = TagLib::EXIF_MAIN;
			break;
		case FIMD_EXIF_EXIF:
			internal_md_model = TagLib::EXIF_EXIF;
			break;
		case FIMD_EXIF_GPS:
			internal_md_model = TagLib::EXIF_GPS;
			break;
		case FIMD_EXIF_INTEROP:
			internal_md_model = TagLib::EXIF_INTEROP;
			break;
		default:
			return FALSE;
	}

	try {
		// 1) according to the TIFF specifications, 
		// the entries in a TIF IFD must be sorted in ascending order by tag id

		// store the tags into a vector
		vTagList.reserve(metadata_count);
		mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag);
		if(mdhandle) {
			// parse the tags and store them inside vTagList
			do {
				// rewrite the tag id using FreeImage internal database
				// (in case the tag id is wrong or missing)
				const char *key = FreeImage_GetTagKey(tag);
				int tag_id = s.getTagID(internal_md_model, key);
				if(tag_id != -1) {
					// this is a known tag, set the tag ID
					FreeImage_SetTagID(tag, (WORD)tag_id);
					// record the tag
					vTagList.push_back(tag);
				}
				// else ignore this tag
			} while(FreeImage_FindNextMetadata(mdhandle, &tag));

			FreeImage_FindCloseMetadata(mdhandle);

			// sort the vector by tag id
			std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare());

			// update the metadata_count
			metadata_count = (unsigned)vTagList.size();

		} else {
			throw(1);
		}

		// 2) prepare the place for each IFD entries.

		/*
		An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), 
		followed by a sequence of 12-byte field entries, 
		followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD.
		*/

		{		
			// prepare place for 2 bytes for number of entries + 12 bytes for each entry
			unsigned ifd_size = 2 + 12 * metadata_count;
			FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem);
			// record the offset used to write values > 4-bytes
			ifd_offset = FreeImage_TellMemory(hmem);
			// rewind
			FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET);
		}

		// 3) write each IFD entry in tag id ascending order

		// number of directory entries
		WORD nde = (WORD)metadata_count;
		FreeImage_WriteMemory(&nde, 1, 2, hmem);

		// for each entry ...
		for(unsigned i = 0; i < metadata_count; i++) {
			FITAG *tag = vTagList[i];
			// tag id
			WORD tag_id = FreeImage_GetTagID(tag);
			FreeImage_WriteMemory(&tag_id, 1, 2, hmem);
			// tag type (compliant with TIFF specification)
			WORD tag_type = (WORD)FreeImage_GetTagType(tag);
			FreeImage_WriteMemory(&tag_type, 1, 2, hmem);
			// tag count
			DWORD tag_count = FreeImage_GetTagCount(tag);
			FreeImage_WriteMemory(&tag_count, 1, 4, hmem);
			// tag value or offset (results are in BYTE's units)
			unsigned tag_length = FreeImage_GetTagLength(tag);
			if(tag_length <= 4) {
				// 4 bytes or less, write the value (left justified)
				const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag);
				FreeImage_WriteMemory(tag_value, 1, tag_length, hmem);
				for(unsigned k = tag_length; k < 4; k++) {
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
			} else {
				// write an offset
				FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem);
				// write the value
				long current_position = FreeImage_TellMemory(hmem);
				FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
				FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem);
				if(tag_length & 1) {
					// align to the next WORD boundary
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
				// next offset to use
				ifd_offset = FreeImage_TellMemory(hmem);
				// rewind
				FreeImage_SeekMemory(hmem, current_position, SEEK_SET);
			}
		}

		// end-of-IFD or next IFD (0 == none)
		FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
		FreeImage_WriteMemory(&empty_byte, 1, 4, hmem);

		return TRUE;
	}
	catch(int) {
		return FALSE;
	}
}
Пример #3
0
/**
Process a standard Exif tag
*/
static void 
processExifTag(FIBITMAP *dib, FITAG *tag, char *pval, BOOL msb_order, TagLib::MDMODEL md_model) {
	char defaultKey[16];
	int n;
	DWORD i;

	// allocate a buffer to store the tag value
	BYTE *exif_value = (BYTE*)malloc(FreeImage_GetTagLength(tag) * sizeof(BYTE));
	memset(exif_value, 0, FreeImage_GetTagLength(tag) * sizeof(BYTE));

	// get the tag value
	switch(FreeImage_GetTagType(tag)) {

		case FIDT_SHORT:
		{
			WORD *value = (WORD*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadUint16(msb_order, pval + i * sizeof(WORD));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SSHORT:
		{
			short *value = (short*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadInt16(msb_order, pval + i * sizeof(short));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_LONG:
		{
			DWORD *value = (DWORD*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadUint32(msb_order, pval + i * sizeof(DWORD));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SLONG:
		{
			LONG *value = (LONG*)&exif_value[0];
			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
				value[i] = ReadInt32(msb_order, pval + i * sizeof(LONG));
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_RATIONAL:
		{
			n = sizeof(DWORD);

			DWORD *value = (DWORD*)&exif_value[0];						
			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
				// read a sequence of (numerator, denominator)
				value[i] = ReadUint32(msb_order, n*i + (char*)pval);
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_SRATIONAL:
		{
			n = sizeof(LONG);

			LONG *value = (LONG*)&exif_value[0];
			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
				// read a sequence of (numerator, denominator)
				value[i] = ReadInt32(msb_order, n*i + (char*)pval);
			}
			FreeImage_SetTagValue(tag, value);
			break;
		}
		case FIDT_BYTE:
		case FIDT_ASCII:
		case FIDT_SBYTE:
		case FIDT_UNDEFINED:
		case FIDT_FLOAT:
		case FIDT_DOUBLE:
		default:
			FreeImage_SetTagValue(tag, pval);
			break;
	}

	if(md_model == TagLib::EXIF_MAKERNOTE_CANON) {
		// A single Canon tag can have multiple values within
		processCanonMakerNoteTag(dib, tag);
	}
	else {
		TagLib& s = TagLib::instance();

		WORD tag_id = FreeImage_GetTagID(tag);

		// get the tag key and description
		const char *key = s.getTagFieldName(md_model, tag_id, defaultKey);
		FreeImage_SetTagKey(tag, key);
		const char *description = s.getTagDescription(md_model, tag_id);
		FreeImage_SetTagDescription(tag, description);

		// store the tag
		if(key) {
			FreeImage_SetMetadata(s.getFreeImageModel(md_model), dib, key, tag);
		}
	}
	

	// free the temporary buffer
	free(exif_value);

}
Пример #4
0
/**
	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;
}
Пример #5
0
/**
Convert a tag to a C string
*/
static const char* 
ConvertAnyTag(FITAG *tag) {
	char format[MAX_TEXT_EXTENT];
	static std::string buffer;
	DWORD i;

	if(!tag)
		return NULL;

	buffer.erase();
	
	// convert the tag value to a string buffer

	FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
	DWORD tag_count = FreeImage_GetTagCount(tag);

	switch(tag_type) {
		case FIDT_BYTE:		// N x 8-bit unsigned integer 
		{
			BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld",	(long) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	(long) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SHORT:	// N x 16-bit unsigned integer 
		{
			unsigned short *pvalue = (unsigned short *)FreeImage_GetTagValue(tag);

			sprintf(format, "%hu", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %hu",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_LONG:		// N x 32-bit unsigned integer 
		{
			unsigned long *pvalue = (unsigned long *)FreeImage_GetTagValue(tag);

			sprintf(format, "%lu", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %lu",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_RATIONAL: // N x 64-bit unsigned fraction 
		{
			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
				buffer += format;
			}
			break;
		}
		case FIDT_SBYTE:	// N x 8-bit signed integer 
		{
			char *pvalue = (char*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld",	(long) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	(long) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SSHORT:	// N x 16-bit signed integer 
		{
			short *pvalue = (short *)FreeImage_GetTagValue(tag);

			sprintf(format, "%hd", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %hd",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SLONG:	// N x 32-bit signed integer 
		{
			long *pvalue = (long *)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_SRATIONAL:// N x 64-bit signed fraction 
		{
			LONG *pvalue = (LONG*)FreeImage_GetTagValue(tag);

			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
				buffer += format;
			}
			break;
		}
		case FIDT_FLOAT:	// N x 32-bit IEEE floating point 
		{
			float *pvalue = (float *)FreeImage_GetTagValue(tag);

			sprintf(format, "%f", (double) pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%f", (double) pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_DOUBLE:	// N x 64-bit IEEE floating point 
		{
			double *pvalue = (double *)FreeImage_GetTagValue(tag);

			sprintf(format, "%f", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, "%f", pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_IFD:		// N x 32-bit unsigned integer (offset) 
		{
			unsigned long *pvalue = (unsigned long *)FreeImage_GetTagValue(tag);

			sprintf(format, "%X", pvalue[0]);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " %X",	pvalue[i]);
				buffer += format;
			}
			break;
		}
		case FIDT_PALETTE:	// N x 32-bit RGBQUAD 
		{
			RGBQUAD *pvalue = (RGBQUAD *)FreeImage_GetTagValue(tag);

			sprintf(format, "(%d,%d,%d,%d)", pvalue[0].rgbRed, pvalue[0].rgbGreen, pvalue[0].rgbBlue, pvalue[0].rgbReserved);
			buffer += format;
			for(i = 1; i < tag_count; i++) {
				sprintf(format, " (%d,%d,%d,%d)", pvalue[i].rgbRed, pvalue[i].rgbGreen, pvalue[i].rgbBlue, pvalue[i].rgbReserved);
				buffer += format;
			}
			break;
		}
		case FIDT_ASCII:	// 8-bit bytes w/ last byte null 
		case FIDT_UNDEFINED:// 8-bit untyped data 
		default:
		{
			int max_size = MIN((int)FreeImage_GetTagLength(tag), (int)MAX_TEXT_EXTENT);
			if(max_size == MAX_TEXT_EXTENT)
				max_size--;
			memcpy(format, (char*)FreeImage_GetTagValue(tag), max_size);
			format[max_size] = '\0';
			buffer += format;
			break;
		}
	}

	return buffer.c_str();
}
Пример #6
0
/**
Convert a Exif GPS tag to a C string
*/
static const char* 
ConvertExifGPSTag(FITAG *tag) {
	char format[MAX_TEXT_EXTENT];
	static std::string buffer;

	if(!tag)
		return NULL;

	buffer.erase();

	// convert the tag value to a string buffer

	switch(FreeImage_GetTagID(tag)) {
		case TAG_GPS_LATITUDE:
		case TAG_GPS_LONGITUDE:
		case TAG_GPS_TIME_STAMP:
		{
			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);
			if(FreeImage_GetTagLength(tag) == 24) {
				// dd:mm:ss or hh:mm:ss
				int dd = 0, mm = 0;
				double ss = 0;

				// convert to seconds
				if(pvalue[1])
					ss += ((double)pvalue[0] / (double)pvalue[1]) * 3600;
				if(pvalue[3])
					ss += ((double)pvalue[2] / (double)pvalue[3]) * 60;
				if(pvalue[5])
					ss += ((double)pvalue[4] / (double)pvalue[5]);
				
				// convert to dd:mm:ss.ss
				dd = (int)(ss / 3600);
				mm = (int)(ss / 60) - dd * 60;
				ss = ss - dd * 3600 - mm * 60;

				sprintf(format, "%d:%d:%.2f", dd, mm, ss);
				buffer += format;
				return buffer.c_str();
			}
		}
		break;

		case TAG_GPS_VERSION_ID:
		case TAG_GPS_LATITUDE_REF:
		case TAG_GPS_LONGITUDE_REF:
		case TAG_GPS_ALTITUDE_REF:
		case TAG_GPS_ALTITUDE:
		case TAG_GPS_SATELLITES:
		case TAG_GPS_STATUS:
		case TAG_GPS_MEASURE_MODE:
		case TAG_GPS_DOP:
		case TAG_GPS_SPEED_REF:
		case TAG_GPS_SPEED:
		case TAG_GPS_TRACK_REF:
		case TAG_GPS_TRACK:
		case TAG_GPS_IMG_DIRECTION_REF:
		case TAG_GPS_IMG_DIRECTION:
		case TAG_GPS_MAP_DATUM:
		case TAG_GPS_DEST_LATITUDE_REF:
		case TAG_GPS_DEST_LATITUDE:
		case TAG_GPS_DEST_LONGITUDE_REF:
		case TAG_GPS_DEST_LONGITUDE:
		case TAG_GPS_DEST_BEARING_REF:
		case TAG_GPS_DEST_BEARING:
		case TAG_GPS_DEST_DISTANCE_REF:
		case TAG_GPS_DEST_DISTANCE:
		case TAG_GPS_PROCESSING_METHOD:
		case TAG_GPS_AREA_INFORMATION:
		case TAG_GPS_DATE_STAMP:
		case TAG_GPS_DIFFERENTIAL:
			break;
	}

	return ConvertAnyTag(tag);
}
Пример #7
0
/**
Convert a Exif tag to a C string
*/
static const char* 
ConvertExifTag(FITAG *tag) {
	char format[MAX_TEXT_EXTENT];
	static std::string buffer;

	if(!tag)
		return NULL;

	buffer.erase();

	// convert the tag value to a string buffer

	switch(FreeImage_GetTagID(tag)) {
		case TAG_ORIENTATION:
		{
			unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch (orientation) {
				case 1:
					return "top, left side";
				case 2:
					return "top, right side";
				case 3:
					return "bottom, right side";
				case 4:
					return "bottom, left side";
				case 5:
					return "left side, top";
				case 6:
					return "right side, top";
				case 7:
					return "right side, bottom";
				case 8:
					return "left side, bottom";
				default:
					break;
			}
		}
		break;

		case TAG_REFERENCE_BLACK_WHITE:
		{
			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);
			if(FreeImage_GetTagLength(tag) == 48) {
				// reference black point value and reference white point value (ReferenceBlackWhite)
				int blackR = 0, whiteR = 0, blackG = 0, whiteG = 0, blackB = 0, whiteB = 0;
				if(pvalue[1])
					blackR = (int)(pvalue[0] / pvalue[1]);
				if(pvalue[3])
					whiteR = (int)(pvalue[2] / pvalue[3]);
				if(pvalue[5])
					blackG = (int)(pvalue[4] / pvalue[5]);
				if(pvalue[7])
					whiteG = (int)(pvalue[6] / pvalue[7]);
				if(pvalue[9])
					blackB = (int)(pvalue[8] / pvalue[9]);
				if(pvalue[11])
					whiteB = (int)(pvalue[10] / pvalue[11]);

				sprintf(format, "[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB);
				buffer += format;
				return buffer.c_str();
			}

		}
		break;

		case TAG_COLOR_SPACE:
		{
			unsigned short colorSpace = *((unsigned short *)FreeImage_GetTagValue(tag));
			if (colorSpace == 1) {
				return "sRGB";
			} else if (colorSpace == 65535) {
				return "Undefined";
			} else {
				return "Unknown";
			}
		}
		break;

		case TAG_COMPONENTS_CONFIGURATION:
		{
			char *componentStrings[7] = {"", "Y", "Cb", "Cr", "R", "G", "B"};
			BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag);
			for(DWORD i = 0; i < MIN((DWORD)4, FreeImage_GetTagCount(tag)); i++) {
				int j = pvalue[i];
				if(j > 0 && j < 7)
					buffer += componentStrings[j];
			}
			return buffer.c_str();
		}
		break;

		case TAG_COMPRESSED_BITS_PER_PIXEL:
		{
			FIRational r(tag);
			buffer = r.toString();
			if(buffer == "1")
				buffer += " bit/pixel";
			else 
				buffer += " bits/pixel";
			return buffer.c_str();
		}
		break;

		case TAG_X_RESOLUTION:
		case TAG_Y_RESOLUTION:
		case TAG_FOCAL_PLANE_X_RES:
		case TAG_FOCAL_PLANE_Y_RES:
		case TAG_BRIGHTNESS_VALUE:
		case TAG_EXPOSURE_BIAS_VALUE:
		{
			FIRational r(tag);
			buffer = r.toString();
			return buffer.c_str();
		}
		break;

		case TAG_RESOLUTION_UNIT:
		case TAG_FOCAL_PLANE_UNIT:
		{
			unsigned short resolutionUnit = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch (resolutionUnit) {
				case 1:
					return "(No unit)";
				case 2:
					return "inches";
				case 3:
					return "cm";
				default:
					break;
			}
		}
		break;

		case TAG_YCBCR_POSITIONING:
		{
			unsigned short yCbCrPosition = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch (yCbCrPosition) {
				case 1:
					return "Center of pixel array";
				case 2:
					return "Datum point";
				default:
					break;
			}
		} 
		break;

		case TAG_EXPOSURE_TIME:
		{
			FIRational r(tag);
			buffer = r.toString();
			buffer += " sec";
			return buffer.c_str();
		}
		break;

		case TAG_SHUTTER_SPEED_VALUE:
		{
			FIRational r(tag);
			long apexValue = r.longValue();
			long apexPower = 1 << apexValue;
			sprintf(format, "1/%d sec", (int)apexPower);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_APERTURE_VALUE:
		case TAG_MAX_APERTURE_VALUE:
		{
			FIRational r(tag);
			double apertureApex = r.doubleValue();
	        double rootTwo = sqrt((double)2);
			double fStop = pow(rootTwo, apertureApex);
			sprintf(format, "F%.1f", fStop);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_FNUMBER:
		{
			FIRational r(tag);
			double fnumber = r.doubleValue();
			sprintf(format, "F%.1f", fnumber);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_FOCAL_LENGTH:
		{
			FIRational r(tag);
			double focalLength = r.doubleValue();
			sprintf(format, "%.1f mm", focalLength);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_FOCAL_LENGTH_IN_35MM_FILM:
		{
			unsigned short focalLength = *((unsigned short *)FreeImage_GetTagValue(tag));
			sprintf(format, "%hu mm", focalLength);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_FLASH:
		{
			unsigned short flash = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch(flash) {
				case 0x0000:
					return "Flash did not fire";
				case 0x0001:
					return "Flash fired";
				case 0x0005:
					return "Strobe return light not detected";
				case 0x0007:
					return "Strobe return light detected";
				case 0x0009:
					return "Flash fired, compulsory flash mode";
				case 0x000D:
					return "Flash fired, compulsory flash mode, return light not detected";
				case 0x000F:
					return "Flash fired, compulsory flash mode, return light detected";
				case 0x0010:
					return "Flash did not fire, compulsory flash mode";
				case 0x0018:
					return "Flash did not fire, auto mode";
				case 0x0019:
					return "Flash fired, auto mode";
				case 0x001D:
					return "Flash fired, auto mode, return light not detected";
				case 0x001F:
					return "Flash fired, auto mode, return light detected";
				case 0x0020:
					return "No flash function";
				case 0x0041:
					return "Flash fired, red-eye reduction mode";
				case 0x0045:
					return "Flash fired, red-eye reduction mode, return light not detected";
				case 0x0047:
					return "Flash fired, red-eye reduction mode, return light detected";
				case 0x0049:
					return "Flash fired, compulsory flash mode, red-eye reduction mode";
				case 0x004D:
					return "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected";
				case 0x004F:
					return "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected";
				case 0x0059:
					return "Flash fired, auto mode, red-eye reduction mode";
				case 0x005D:
					return "Flash fired, auto mode, return light not detected, red-eye reduction mode";
				case 0x005F:
					return "Flash fired, auto mode, return light detected, red-eye reduction mode";
				default:
					sprintf(format, "Unknown (%d)", flash);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_SCENE_TYPE:
		{
			BYTE sceneType = *((BYTE*)FreeImage_GetTagValue(tag));
			if (sceneType == 1) {
				return "Directly photographed image";
			} else {
				sprintf(format, "Unknown (%d)", sceneType);
				buffer += format;
				return buffer.c_str();
			}
		}
		break;

		case TAG_SUBJECT_DISTANCE:
		{
			FIRational r(tag);
			if(r.getNumerator() == 0xFFFFFFFF) {
				return "Infinity";
			} else if(r.getNumerator() == 0) {
				return "Distance unknown";
			} else {
				double distance = r.doubleValue();
				sprintf(format, "%.3f meters", distance);
				buffer += format;
				return buffer.c_str();
			}
		}
		break;
			
		case TAG_METERING_MODE:
		{
			unsigned short meteringMode = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch (meteringMode) {
				case 0:
					return "Unknown";
				case 1:
					return "Average";
				case 2:
					return "Center weighted average";
				case 3:
					return "Spot";
				case 4:
					return "Multi-spot";
				case 5:
					return "Multi-segment";
				case 6:
					return "Partial";
				case 255:
					return "(Other)";
				default:
					return "";
			}
		}
		break;

		case TAG_LIGHT_SOURCE:
		{
			unsigned short lightSource = *((unsigned short *)FreeImage_GetTagValue(tag));
			switch (lightSource) {
				case 0:
					return "Unknown";
				case 1:
					return "Daylight";
				case 2:
					return "Fluorescent";
				case 3:
					return "Tungsten (incandescent light)";
				case 4:
					return "Flash";
				case 9:
					return "Fine weather";
				case 10:
					return "Cloudy weather";
				case 11:
					return "Shade";
				case 12:
					return "Daylight fluorescent (D 5700 - 7100K)";
				case 13:
					return "Day white fluorescent (N 4600 - 5400K)";
				case 14:
					return "Cool white fluorescent (W 3900 - 4500K)";
				case 15:
					return "White fluorescent (WW 3200 - 3700K)";
				case 17:
					return "Standard light A";
				case 18:
					return "Standard light B";
				case 19:
					return "Standard light C";
				case 20:
					return "D55";
				case 21:
					return "D65";
				case 22:
					return "D75";
				case 23:
					return "D50";
				case 24:
					return "ISO studio tungsten";
				case 255:
					return "(Other)";
				default:
					return "";
			}
		}
		break;

		case TAG_SENSING_METHOD:
		{
			unsigned short sensingMethod = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (sensingMethod) {
				case 1:
					return "(Not defined)";
				case 2:
					return "One-chip color area sensor";
				case 3:
					return "Two-chip color area sensor";
				case 4:
					return "Three-chip color area sensor";
				case 5:
					return "Color sequential area sensor";
				case 7:
					return "Trilinear sensor";
				case 8:
					return "Color sequential linear sensor";
				default:
					return "";
			}
		}
		break;

		case TAG_FILE_SOURCE:
		{
			BYTE fileSource = *((BYTE*)FreeImage_GetTagValue(tag));
			if (fileSource == 3) {
				return "Digital Still Camera (DSC)";
			} else {
				sprintf(format, "Unknown (%d)", fileSource);
				buffer += format;
				return buffer.c_str();
			}
        }
		break;

		case TAG_EXPOSURE_PROGRAM:
		{
			unsigned short exposureProgram = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (exposureProgram) {
				case 1:
					return "Manual control";
				case 2:
					return "Program normal";
				case 3:
					return "Aperture priority";
				case 4:
					return "Shutter priority";
				case 5:
					return "Program creative (slow program)";
				case 6:
					return "Program action (high-speed program)";
				case 7:
					return "Portrait mode";
				case 8:
					return "Landscape mode";
				default:
					sprintf(format, "Unknown program (%d)", exposureProgram);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_CUSTOM_RENDERED:
		{
			unsigned short customRendered = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (customRendered) {
				case 0:
					return "Normal process";
				case 1:
					return "Custom process";
				default:
					sprintf(format, "Unknown rendering (%d)", customRendered);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_EXPOSURE_MODE:
		{
			unsigned short exposureMode = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (exposureMode) {
				case 0:
					return "Auto exposure";
				case 1:
					return "Manual exposure";
				case 2:
					return "Auto bracket";
				default:
					sprintf(format, "Unknown mode (%d)", exposureMode);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_WHITE_BALANCE:
		{
			unsigned short whiteBalance = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (whiteBalance) {
				case 0:
					return "Auto white balance";
				case 1:
					return "Manual white balance";
				default:
					sprintf(format, "Unknown (%d)", whiteBalance);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_SCENE_CAPTURE_TYPE:
		{
			unsigned short sceneType = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (sceneType) {
				case 0:
					return "Standard";
				case 1:
					return "Landscape";
				case 2:
					return "Portrait";
				case 3:
					return "Night scene";
				default:
					sprintf(format, "Unknown (%d)", sceneType);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_GAIN_CONTROL:
		{
			unsigned short gainControl = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (gainControl) {
				case 0:
					return "None";
				case 1:
					return "Low gain up";
				case 2:
					return "High gain up";
				case 3:
					return "Low gain down";
				case 4:
					return "High gain down";
				default:
					sprintf(format, "Unknown (%d)", gainControl);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_CONTRAST:
		{
			unsigned short contrast = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (contrast) {
				case 0:
					return "Normal";
				case 1:
					return "Soft";
				case 2:
					return "Hard";
				default:
					sprintf(format, "Unknown (%d)", contrast);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_SATURATION:
		{
			unsigned short saturation = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (saturation) {
				case 0:
					return "Normal";
				case 1:
					return "Low saturation";
				case 2:
					return "High saturation";
				default:
					sprintf(format, "Unknown (%d)", saturation);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_SHARPNESS:
		{
			unsigned short sharpness = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (sharpness) {
				case 0:
					return "Normal";
				case 1:
					return "Soft";
				case 2:
					return "Hard";
				default:
					sprintf(format, "Unknown (%d)", sharpness);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_SUBJECT_DISTANCE_RANGE:
		{
			unsigned short distanceRange = *((unsigned short *)FreeImage_GetTagValue(tag));

			switch (distanceRange) {
				case 0:
					return "unknown";
				case 1:
					return "Macro";
				case 2:
					return "Close view";
				case 3:
					return "Distant view";
				default:
					sprintf(format, "Unknown (%d)", distanceRange);
					buffer += format;
					return buffer.c_str();
			}
		}
		break;

		case TAG_ISO_SPEED_RATINGS:
		{
			unsigned short isoEquiv = *((unsigned short *)FreeImage_GetTagValue(tag));
			if (isoEquiv < 50) {
				isoEquiv *= 200;
			}
			sprintf(format, "%d", isoEquiv);
			buffer += format;
			return buffer.c_str();
		}
		break;

		case TAG_USER_COMMENT:
		{
			// first 8 bytes are used to define an ID code
			// we assume this is an ASCII string
			const BYTE *userComment = (BYTE*)FreeImage_GetTagValue(tag);
			for(DWORD i = 8; i < FreeImage_GetTagLength(tag); i++) {
				buffer += userComment[i];
			}
			buffer += '\0';
			return buffer.c_str();
		}
		break;

	}

	return ConvertAnyTag(tag);
}
Пример #8
0
/**
Process Exif directory

@param dib Input FIBITMAP
@param tiffp Pointer to the TIFF header
@param dwOffsetIfd0 Offset to the 0th IFD (first IFD)
@param dwLength Length of the Exif file
@param dwProfileOffset File offset to be used when reading 'offset/value' tags
@param msb_order Endianness order of the Exif file (TRUE if big-endian, FALSE if little-endian)
@param starting_md_model Metadata model of the IFD (should be TagLib::EXIF_MAIN for a jpeg)
@return Returns TRUE if sucessful, returns FALSE otherwise
*/
static BOOL 
jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, DWORD dwOffsetIfd0, DWORD dwLength, DWORD dwProfileOffset, BOOL msb_order, TagLib::MDMODEL starting_md_model) {
	WORD de, nde;

	std::stack<WORD>			destack;	// directory entries stack
	std::stack<const BYTE*>		ifdstack;	// IFD stack
	std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack

	// Keep a list of already visited IFD to avoid stack overflows 
	// when recursive/cyclic directory structures exist. 
	// This kind of recursive Exif file was encountered with Kodak images coming from 
	// KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
	std::map<DWORD, int> visitedIFD;

	/*
	"An Image File Directory (IFD) consists of a 2-byte count of the number of directory
	entries (i.e. the number of fields), followed by a sequence of 12-byte field
	entries, followed by a 4-byte offset of the next IFD (or 0 if none)."
	The "next IFD" (1st IFD) is the thumbnail.
	*/
	#define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))

	// set the metadata model to Exif

	TagLib::MDMODEL md_model = starting_md_model;

	// set the pointer to the first IFD (0th IFD) and follow it were it leads.

	const BYTE *ifd0th = (BYTE*)tiffp + (size_t)dwOffsetIfd0;

	const BYTE *ifdp = ifd0th;

	de = 0;

	do {
		// if there is anything on the stack then pop it off
		if(!destack.empty()) {
			ifdp		= ifdstack.top();	ifdstack.pop();
			de			= destack.top();	destack.pop();
			md_model	= modelstack.top();	modelstack.pop();
		}

		// remember that we've visited this directory and entry so that we don't visit it again later
		DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
		if(visitedIFD.find(visited) != visitedIFD.end()) {
			continue;
		} else {
			visitedIFD[visited] = 1;	// processed
		}

		// determine how many entries there are in the current IFD
		nde = ReadUint16(msb_order, ifdp);
		if (((size_t)(ifdp - tiffp) + 12 * nde) > (size_t)dwLength) {
			// suspicious IFD offset, ignore
			continue;
		}

		for(; de < nde; de++) {
			char *pde = NULL;	// pointer to the directory entry
			char *pval = NULL;	// pointer to the tag value
			
			// create a tag
			FITAG *tag = FreeImage_CreateTag();
			if(!tag) return FALSE;

			// point to the directory entry
			pde = (char*) DIR_ENTRY_ADDR(ifdp, de);

			// get the tag ID
			WORD tag_id = ReadUint16(msb_order, pde);
			FreeImage_SetTagID(tag, tag_id);

			// get the tag type
			WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
                // a problem occured : delete the tag (not free'd after)
			    FreeImage_DeleteTag(tag);
				// break out of the for loop
				break;
            }
			FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);

			// get number of components
			DWORD tag_count = ReadUint32(msb_order, pde + 4);
			FreeImage_SetTagCount(tag, tag_count);

            // check that tag length (size of the tag value in bytes) will fit in a DWORD
            unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag));
            if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) {
                FreeImage_DeleteTag(tag);
                // jump to next entry
                continue;
            }
			FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * tag_data_width);

			if(FreeImage_GetTagLength(tag) <= 4) {
				// 4 bytes or less and value is in the dir entry itself
				pval = pde + 8;
			} else {
				// if its bigger than 4 bytes, the directory entry contains an offset				
				DWORD offset_value = ReadUint32(msb_order, pde + 8);
				// the offset can be relative to tiffp or to an external reference (see JPEG-XR)
				if(dwProfileOffset) {
					offset_value -= dwProfileOffset;
				}
				// first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
				if(offset_value > dwLength) {
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				// now check that length does not exceed the buffer size
				if(FreeImage_GetTagLength(tag) > dwLength - offset_value){
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				pval = (char*)(tiffp + offset_value);
			}

			// check for a IFD offset
			BOOL isIFDOffset = FALSE;
			switch(FreeImage_GetTagID(tag)) {
				case TAG_EXIF_OFFSET:
				case TAG_GPS_OFFSET:
				case TAG_INTEROP_OFFSET:
				case TAG_MAKER_NOTE:
					isIFDOffset = TRUE;
					break;
			}
			if(isIFDOffset)	{
				DWORD sub_offset = 0;
				TagLib::MDMODEL next_mdmodel = md_model;
				const BYTE *next_ifd = ifdp;
				
				// get offset and metadata model
				if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
					processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)pval + sub_offset;
				} else {
					processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)tiffp + sub_offset;
				}

				if((sub_offset < dwLength) && (next_mdmodel != TagLib::UNKNOWN)) {
					// push our current directory state onto the stack
					ifdstack.push(ifdp);
					// jump to the next entry
					de++;
					destack.push(de);

					// push our current metadata model
					modelstack.push(md_model);

					// push new state onto of stack to cause a jump
					ifdstack.push(next_ifd);
					destack.push(0);

					// select a new metadata model
					modelstack.push(next_mdmodel);
					
					// delete the tag as it won't be stored nor deleted in the for() loop
					FreeImage_DeleteTag(tag);
					
					break; // break out of the for loop
				}
				else {
					// unsupported camera model, canon maker tag or something unknown
					// process as a standard tag
					processExifTag(dib, tag, pval, msb_order, md_model);
				}			

			} else {
				// process as a standard tag
				processExifTag(dib, tag, pval, msb_order, md_model);
			}
			
			// delete the tag
			FreeImage_DeleteTag(tag);

        } // for(nde)

		// additional thumbnail data is skipped

    } while (!destack.empty()); 

	//
	// --- handle thumbnail data ---
	//

	const WORD entriesCount0th = ReadUint16(msb_order, ifd0th);
	
	DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th));
	if((next_offset == 0) || (next_offset >= dwLength)) {
		return TRUE; //< no thumbnail
	}
	
	const BYTE* const ifd1st = (BYTE*)tiffp + next_offset;
	const WORD entriesCount1st = ReadUint16(msb_order, ifd1st);
	
	unsigned thCompression = 0;
	unsigned thOffset = 0; 
	unsigned thSize = 0; 
	
	for(int e = 0; e < entriesCount1st; e++) {

		// point to the directory entry
		const BYTE* base = DIR_ENTRY_ADDR(ifd1st, e);
		
		// check for buffer overflow
		const size_t remaining = (size_t)base + 12 - (size_t)tiffp;
		if(remaining >= dwLength) {
			// bad IFD1 directory, ignore it
			return FALSE;
		}

		// get the tag ID
		WORD tag = ReadUint16(msb_order, base);
		// get the tag type
		/*WORD type = */ReadUint16(msb_order, base + sizeof(WORD));
		// get number of components
		/*DWORD count = */ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
		// get the tag value
		DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD));

		switch(tag) {
			case TAG_COMPRESSION:
				// Tiff Compression Tag (should be COMPRESSION_OJPEG (6), but is not always respected)
				thCompression = offset;
				break;
			case TAG_JPEG_INTERCHANGE_FORMAT:
				// Tiff JPEGInterchangeFormat Tag
				thOffset = offset;
				break;
			case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
				// Tiff JPEGInterchangeFormatLength Tag
				thSize = offset;
				break;
			// ### X and Y Resolution ignored, orientation ignored
			case TAG_X_RESOLUTION:		// XResolution
			case TAG_Y_RESOLUTION:		// YResolution
			case TAG_RESOLUTION_UNIT:	// ResolutionUnit
			case TAG_ORIENTATION:		// Orientation
				break;
			default:
				break;
		}
	}
	
	if(/*thCompression != 6 ||*/ thOffset == 0 || thSize == 0) {
		return TRUE;
	}
	
	if(thOffset + thSize > dwLength) {
		return TRUE;
	}
	
	// load the thumbnail

	const BYTE *thLocation = tiffp + thOffset;
	
	FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(thLocation), thSize);
	FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
	FreeImage_CloseMemory(hmem);
	
	// store the thumbnail
	FreeImage_SetThumbnail(dib, thumbnail);
	// then delete it
	FreeImage_Unload(thumbnail);

	return TRUE;
}
Пример #9
0
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;
	}
}
Пример #10
0
BOOL DLL_CALLCONV 
FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
	if(!dib) 
		return FALSE;

	TAGMAP *tagmap = NULL;

	// get the metadata model
	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
	METADATAMAP::iterator model_iterator = metadata->find(model);
	if (model_iterator != metadata->end()) {
		tagmap = model_iterator->second;
	}

	if(key != NULL) {

		if(!tagmap) {
			// this model, doesn't exist: create it 
			tagmap = new(std::nothrow) TAGMAP();
			(*metadata)[model] = tagmap;
		}
		
		if(tag) {
			// first check the tag
			if(FreeImage_GetTagKey(tag) == NULL) {
				FreeImage_SetTagKey(tag, key);
			} else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
				// set the tag key
				FreeImage_SetTagKey(tag, key);
			}
			if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
				FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key);
				return FALSE;
			}

			// fill the tag ID if possible and if it's needed
			TagLib& tag_lib = TagLib::instance();
			switch(model) {
				case FIMD_IPTC:
				{
					int id = tag_lib.getTagID(TagLib::IPTC, key);
					/*
					if(id == -1) {
						FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key);
					}
					*/
					FreeImage_SetTagID(tag, (WORD)id);
				}
				break;

				default:
					break;
			}

			// delete existing tag
			FITAG *old_tag = (*tagmap)[key];
			if(old_tag) {
				FreeImage_DeleteTag(old_tag);
			}

			// create a new tag
			(*tagmap)[key] = FreeImage_CloneTag(tag);
		}
		else {
			// delete existing tag
			TAGMAP::iterator i = tagmap->find(key);
			if(i != tagmap->end()) {
				FITAG *old_tag = (*i).second;
				FreeImage_DeleteTag(old_tag);
				tagmap->erase(key);
			}
		}
	}
	else {
		// destroy the metadata model
		if(tagmap) {
			for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
				FITAG *tag = (*i).second;
				FreeImage_DeleteTag(tag);
			}

			delete tagmap;
			metadata->erase(model_iterator);
		}
	}

	return TRUE;
}
Пример #11
0
/**
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;
	}
}