changeset 449:115db540bd0c

read color profiles from jpeg also with Exiv2
author nadvornik
date Sun, 20 Apr 2008 21:35:03 +0000
parents a73cc0fa14d0
children d643890c48d0
files src/bar_exif.c src/bar_info.c src/cache-loader.c src/exif-common.c src/exif.c src/exif.h src/exiv2.cc src/image-overlay.c src/image.c src/pan-view.c
diffstat 10 files changed, 248 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/src/bar_exif.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/bar_exif.c	Sun Apr 20 21:35:03 2008 +0000
@@ -176,7 +176,7 @@
 	ExifData *exif;
 	gint i;
 
-	exif = exif_read_fd(eb->fd, FALSE);
+	exif = exif_read_fd(eb->fd);
 
 	if (!exif)
 		{
--- a/src/bar_info.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/bar_info.c	Sun Apr 20 21:35:03 2008 +0000
@@ -249,7 +249,7 @@
 
 static gint comment_xmp_read(FileData *fd, GList **keywords, gchar **comment)
 {
-	ExifData *exif = exif_read_fd(fd, FALSE);
+	ExifData *exif = exif_read_fd(fd);
 	gint success;
 	if (!exif) return FALSE;
 
@@ -286,7 +286,7 @@
 {
 	gint success = FALSE;
 	GList *work = keywords;
-	ExifData *exif = exif_read_fd(fd, FALSE);
+	ExifData *exif = exif_read_fd(fd);
 	if (!exif) return FALSE;
 
 	ExifItem *item = exif_get_item(exif, comment_key);
--- a/src/cache-loader.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/cache-loader.c	Sun Apr 20 21:35:03 2008 +0000
@@ -125,7 +125,7 @@
 		time_t date = -1;
 		ExifData *exif;
 
-		exif = exif_read_fd(cl->fd, FALSE);
+		exif = exif_read_fd(cl->fd);
 		if (exif)
 			{
 			gchar *text;
--- a/src/exif-common.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/exif-common.c	Sun Apr 20 21:35:03 2008 +0000
@@ -387,8 +387,10 @@
 		{
 		const gchar *name = "";
 		const gchar *source = "";
-		ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
-		if (!prof_item)
+		unsigned char *profile_data;
+		guint profile_len;
+		profile_data = exif_get_color_profile(exif, &profile_len);
+		if (!profile_data)
 			{
 			gint cs;
 			gchar *interop_index;
@@ -410,28 +412,21 @@
 
 			g_free(interop_index);
 			}
-
-		if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
+		else
 			{
 			source = _("embedded");
 #ifdef HAVE_LCMS
 
 				{
-				unsigned char *data;
-				guint data_len;
 				cmsHPROFILE profile;
 
-				data = (unsigned char *) exif_item_get_data(prof_item, &data_len);
-				if (data)
+				profile = cmsOpenProfileFromMem(profile_data, profile_len);
+				if (profile)
 					{
-					profile = cmsOpenProfileFromMem(data, data_len);
-					if (profile)
-						{
-						name = cmsTakeProductName(profile);
-						cmsCloseProfile(profile);
-						}
-					g_free(data);
+					name = cmsTakeProductName(profile);
+					cmsCloseProfile(profile);
 					}
+				g_free(profile_data);
 				}
 #endif
 			}
@@ -492,7 +487,7 @@
 	return NULL;
 }
 
-ExifData *exif_read_fd(FileData *fd, gint parse_color_profile)
+ExifData *exif_read_fd(FileData *fd)
 {
 	GList *work;
 	gchar *sidecar_path = NULL;
@@ -517,5 +512,142 @@
 
 
 	// FIXME: some caching would be nice
-	return exif_read(fd->path, sidecar_path, parse_color_profile);
+	return exif_read(fd->path, sidecar_path);
+}
+
+
+
+/* embedded icc in jpeg */
+
+
+#define JPEG_MARKER		0xFF
+#define JPEG_MARKER_SOI		0xD8
+#define JPEG_MARKER_EOI		0xD9
+#define JPEG_MARKER_APP1	0xE1
+#define JPEG_MARKER_APP2	0xE2
+
+/* jpeg container format:
+     all data markers start with 0XFF
+     2 byte long file start and end markers: 0xFFD8(SOI) and 0XFFD9(EOI)
+     4 byte long data segment markers in format: 0xFFTTSSSSNNN...
+       FF:   1 byte standard marker identifier
+       TT:   1 byte data type
+       SSSS: 2 bytes in Motorola byte alignment for length of the data.
+	     This value includes these 2 bytes in the count, making actual
+	     length of NN... == SSSS - 2.
+       NNN.: the data in this segment
+ */
+
+gint exif_jpeg_segment_find(unsigned char *data, guint size,
+				   guchar app_marker, const gchar *magic, guint magic_len,
+				   guint *seg_offset, guint *seg_length)
+{
+	guchar marker = 0;
+	guint offset = 0;
+	guint length = 0;
+
+	while (marker != app_marker &&
+	       marker != JPEG_MARKER_EOI)
+		{
+		offset += length;
+		length = 2;
+
+		if (offset + 2 >= size ||
+		    data[offset] != JPEG_MARKER) return FALSE;
+
+		marker = data[offset + 1];
+		if (marker != JPEG_MARKER_SOI &&
+		    marker != JPEG_MARKER_EOI)
+			{
+			if (offset + 4 >= size) return FALSE;
+			length += ((guint)data[offset + 2] << 8) + data[offset + 3];
+			}
+		}
+
+	if (marker == app_marker &&
+	    offset + length < size &&
+	    length >= 4 + magic_len &&
+	    memcmp(data + offset + 4, magic, magic_len) == 0)
+		{
+		*seg_offset = offset + 4;
+		*seg_length = length - 4;
+		return TRUE;
+		}
+
+	return FALSE;
 }
+
+gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size)
+{
+	guint seg_offset = 0;
+	guint seg_length = 0;
+	guint chunk_offset[255];
+	guint chunk_length[255];
+	guint chunk_count = 0;
+
+	/* For jpeg/jfif, ICC color profile data can be in more than one segment.
+	   the data is in APP2 data segments that start with "ICC_PROFILE\x00\xNN\xTT"
+	   NN = segment number for data
+	   TT = total number of ICC segments (TT in each ICC segment should match)
+	 */
+
+	while (exif_jpeg_segment_find(data + seg_offset + seg_length,
+				      size - seg_offset - seg_length,
+				      JPEG_MARKER_APP2,
+				      "ICC_PROFILE\x00", 12,
+				      &seg_offset, &seg_length))
+		{
+		guchar chunk_num;
+		guchar chunk_tot;
+
+		if (seg_length < 14) return FALSE;
+
+		chunk_num = data[seg_offset + 12];
+		chunk_tot = data[seg_offset + 13];
+
+		if (chunk_num == 0 || chunk_tot == 0) return FALSE;
+
+		if (chunk_count == 0)
+			{
+			guint i;
+
+			chunk_count = (guint)chunk_tot;
+			for (i = 0; i < chunk_count; i++) chunk_offset[i] = 0;
+			for (i = 0; i < chunk_count; i++) chunk_length[i] = 0;
+			}
+
+		if (chunk_tot != chunk_count ||
+		    chunk_num > chunk_count) return FALSE;
+
+		chunk_num--;
+		chunk_offset[chunk_num] = seg_offset + 14;
+		chunk_length[chunk_num] = seg_length - 14;
+		}
+
+	if (chunk_count > 0)
+		{
+		unsigned char *cp_data;
+		guint cp_length = 0;
+		guint i;
+
+		for (i = 0; i < chunk_count; i++) cp_length += chunk_length[i];
+		cp_data = g_malloc(cp_length);
+
+		for (i = 0; i < chunk_count; i++)
+			{
+			if (chunk_offset[i] == 0)
+				{
+				/* error, we never saw this chunk */
+				g_free(cp_data);
+				return FALSE;
+				}
+			memcpy(cp_data, data + chunk_offset[i], chunk_length[i]);
+			}
+		if (debug) printf("Found embedded icc profile in jpeg\n");
+		exif_add_jpeg_color_profile(exif, cp_data, cp_length);
+
+		return TRUE;
+		}
+
+	return FALSE;
+}
--- a/src/exif.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/exif.c	Sun Apr 20 21:35:03 2008 +0000
@@ -992,6 +992,7 @@
 	return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list);
 }
 
+
 /*
  *-------------------------------------------------------------------
  * jpeg marker utils
@@ -1015,133 +1016,23 @@
 	     length of NN... == SSSS - 2.
        NNN.: the data in this segment
  */
-
-static gint exif_jpeg_segment_find(unsigned char *data, guint size,
-				   guchar app_marker, const gchar *magic, guint magic_len,
-				   guint *seg_offset, guint *seg_length)
-{
-	guchar marker = 0;
-	guint offset = 0;
-	guint length = 0;
-
-	while (marker != app_marker &&
-	       marker != JPEG_MARKER_EOI)
-		{
-		offset += length;
-		length = 2;
-
-		if (offset + 2 >= size ||
-		    data[offset] != JPEG_MARKER) return FALSE;
-
-		marker = data[offset + 1];
-		if (marker != JPEG_MARKER_SOI &&
-		    marker != JPEG_MARKER_EOI)
-			{
-			if (offset + 4 >= size) return FALSE;
-			length += exif_byte_get_int16(data + offset + 2, EXIF_BYTE_ORDER_MOTOROLA);
-			}
-		}
-
-	if (marker == app_marker &&
-	    offset + length < size &&
-	    length >= 4 + magic_len &&
-	    memcmp(data + offset + 4, magic, magic_len) == 0)
-		{
-		*seg_offset = offset + 4;
-		*seg_length = length - 4;
-		return TRUE;
-		}
-
-	return FALSE;
-}
-
 static ExifMarker jpeg_color_marker = { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.InterColorProfile", NULL, NULL };
 
-static gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size)
+void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
 {
-	guint seg_offset = 0;
-	guint seg_length = 0;
-	guint chunk_offset[255];
-	guint chunk_length[255];
-	guint chunk_count = 0;
-
-	/* For jpeg/jfif, ICC color profile data can be in more than one segment.
-	   the data is in APP2 data segments that start with "ICC_PROFILE\x00\xNN\xTT"
-	   NN = segment number for data
-	   TT = total number of ICC segments (TT in each ICC segment should match)
-	 */
-
-	while (exif_jpeg_segment_find(data + seg_offset + seg_length,
-				      size - seg_offset - seg_length,
-				      JPEG_MARKER_APP2,
-				      "ICC_PROFILE\x00", 12,
-				      &seg_offset, &seg_length))
-		{
-		guchar chunk_num;
-		guchar chunk_tot;
-
-		if (seg_length < 14) return FALSE;
-
-		chunk_num = data[seg_offset + 12];
-		chunk_tot = data[seg_offset + 13];
-
-		if (chunk_num == 0 || chunk_tot == 0) return FALSE;
-
-		if (chunk_count == 0)
-			{
-			guint i;
-
-			chunk_count = (guint)chunk_tot;
-			for (i = 0; i < chunk_count; i++) chunk_offset[i] = 0;
-			for (i = 0; i < chunk_count; i++) chunk_length[i] = 0;
-			}
+	ExifItem *item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1,
+				     &jpeg_color_marker);
+	g_free(item->data);
+	item->data = cp_data;
+	item->elements = cp_length;
+	item->data_len = cp_length;
+	exif->items = g_list_prepend(exif->items, item);
 
-		if (chunk_tot != chunk_count ||
-		    chunk_num > chunk_count) return FALSE;
-
-		chunk_num--;
-		chunk_offset[chunk_num] = seg_offset + 14;
-		chunk_length[chunk_num] = seg_length - 14;
-		}
-
-	if (chunk_count > 0)
-		{
-		ExifItem *item;
-		unsigned char *cp_data;
-		guint cp_length = 0;
-		guint i;
-
-		for (i = 0; i < chunk_count; i++) cp_length += chunk_length[i];
-		cp_data = g_malloc(cp_length);
-
-		for (i = 0; i < chunk_count; i++)
-			{
-			if (chunk_offset[i] == 0)
-				{
-				/* error, we never saw this chunk */
-				g_free(cp_data);
-				return FALSE;
-				}
-			memcpy(cp_data, data + chunk_offset[i], chunk_length[i]);
-			}
-
-		item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1,
-				     &jpeg_color_marker);
-		g_free(item->data);
-		item->data = cp_data;
-		item->elements = cp_length;
-		item->data_len = cp_length;
-		exif->items = g_list_prepend(exif->items, item);
-
-		return TRUE;
-		}
-
-	return FALSE;
 }
 
 static gint exif_jpeg_parse(ExifData *exif,
 			    unsigned char *data, guint size,
-			    ExifMarker *list, gint parse_color)
+			    ExifMarker *list)
 {
 	guint seg_offset = 0;
 	guint seg_length = 0;
@@ -1160,8 +1051,7 @@
 		res = exif_tiff_parse(exif, data + seg_offset + 6, seg_length - 6, list);
 		}
 
-	if (parse_color &&
-	    exif_jpeg_parse_color(exif, data, size))
+	if (exif_jpeg_parse_color(exif, data, size))
 		{
 		res = 0;
 		}
@@ -1169,6 +1059,14 @@
 	return res;
 }
 
+unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len)
+{
+	ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
+	if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
+		return (unsigned char*) exif_item_get_data(prof_item, data_len);
+	return NULL;
+}
+
 
 /*
  *-------------------------------------------------------------------
@@ -1200,8 +1098,6 @@
 	return NULL;
 }
 
-
-
 static gint map_file(const gchar *path, void **mapping, int *size)
 {
 	int fd;
@@ -1262,7 +1158,7 @@
 	g_free(exif);
 }
 
-ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
+ExifData *exif_read(gchar *path, gchar *sidecar_path)
 {
 	ExifData *exif;
 	void *f;
@@ -1284,8 +1180,7 @@
 	exif->current = NULL;
 
 	if ((res = exif_jpeg_parse(exif, (unsigned char *)f, size,
-				   ExifKnownMarkersList,
-				   parse_color_profile)) == -2)
+				   ExifKnownMarkersList)) == -2)
 		{
 		res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList);
 		}
@@ -1308,7 +1203,7 @@
 				break;
 			case FORMAT_RAW_EXIF_JPEG:
 				res = exif_jpeg_parse(exif, (unsigned char*)f + offset, size - offset,
-						      ExifKnownMarkersList, FALSE);
+						      ExifKnownMarkersList);
 				break;
 			case FORMAT_RAW_EXIF_IFD_II:
 			case FORMAT_RAW_EXIF_IFD_MM:
--- a/src/exif.h	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/exif.h	Sun Apr 20 21:35:03 2008 +0000
@@ -105,9 +105,9 @@
  *-----------------------------------------------------------------------------
  */
 
-ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile);
+ExifData *exif_read(gchar *path, gchar *sidecar_path);
 
-ExifData *exif_read_fd(FileData *fd, gint parse_color_profile);
+ExifData *exif_read_fd(FileData *fd);
 
 
 int exif_write(ExifData *exif);
@@ -146,10 +146,23 @@
 int exif_item_delete(ExifData *exif, ExifItem *item);
 int exif_item_set_string(ExifItem *item, const char *str);
 
+unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len);
 
+/* jpeg embedded icc support */
+
+void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length);
+
+
+gint exif_jpeg_segment_find(unsigned char *data, guint size,
+                                   guchar app_marker, const gchar *magic, guint magic_len,
+                                   guint *seg_offset, guint *seg_length);
+gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size);
+
+/*raw support */
 gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
 				    unsigned char *header_data, const guint header_len,
 				    guint *image_offset, guint *exif_offset);
 
 
+
 #endif
--- a/src/exiv2.cc	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/exiv2.cc	Sun Apr 20 21:35:03 2008 +0000
@@ -57,10 +57,15 @@
 #endif
 	bool have_sidecar;
 
+	/* the icc profile in jpeg is not technically exif - store it here */
+	unsigned char *cp_data;
+	guint cp_length;
 
-	_ExifData(gchar *path, gchar *sidecar_path, gint parse_color_profile)
+	_ExifData(gchar *path, gchar *sidecar_path)
 	{
 		have_sidecar = false;
+		cp_data = NULL;
+		cp_length = 0;
 		image = Exiv2::ImageFactory::open(path);
 //		g_assert (image.get() != 0);
 		image->readMetadata();
@@ -74,10 +79,28 @@
 			have_sidecar = sidecar->good();
 			if (debug >= 2) printf("sidecar xmp count %li\n", sidecar->xmpData().count());
 			}
+
+		if (image->mimeType() == std::string("image/jpeg"))
+			{
+			/* try to get jpeg color profile */
+			Exiv2::BasicIo &io = image->io();
+			gint open = io.isopen();
+			if (!open) io.open();
+			unsigned char *mapped = (unsigned char*)io.mmap();
+			if (mapped) exif_jpeg_parse_color(this, mapped, io.size());
+			io.munmap();
+			if (!open) io.close();
+			}
+		
 		
 #endif
 	}
 	
+	~_ExifData()
+	{
+		if (cp_data) g_free(cp_data);
+	}
+	
 	void writeMetadata()
 	{
 		if (have_sidecar) sidecar->writeMetadata();
@@ -105,11 +128,11 @@
 
 extern "C" {
 
-ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
+ExifData *exif_read(gchar *path, gchar *sidecar_path)
 {
 	if (debug) printf("exif read %s,  sidecar: %s\n", path, sidecar_path ? sidecar_path : "-");
 	try {
-		return new ExifData(path, sidecar_path, parse_color_profile);
+		return new ExifData(path, sidecar_path);
 	}
 	catch (Exiv2::AnyError& e) {
 		std::cout << "Caught Exiv2 exception '" << e << "'\n";
@@ -526,6 +549,26 @@
 	}
 }
 
+void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
+{
+	if (exif->cp_data) g_free(exif->cp_data);
+	exif->cp_data = cp_data;
+	exif->cp_length =cp_length;
+}
+
+unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len)
+{
+	if (exif->cp_data)
+		{
+		if (data_len) *data_len = exif->cp_length;
+		return (unsigned char *) g_memdup(exif->cp_data, exif->cp_length);
+		}
+	ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
+	if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
+		return (unsigned char *) exif_item_get_data(prof_item, data_len);
+	return NULL;
+}
+
 
 
 }
--- a/src/image-overlay.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/image-overlay.c	Sun Apr 20 21:35:03 2008 +0000
@@ -137,7 +137,7 @@
 
 	new = g_string_new(str);
 
-	exif = exif_read_fd(imd->image_fd, FALSE);
+	exif = exif_read_fd(imd->image_fd);
 	prev = 0;
 	last = FALSE;
 
--- a/src/image.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/image.c	Sun Apr 20 21:35:03 2008 +0000
@@ -335,7 +335,8 @@
 	ColorManProfileType screen_type;
 	const gchar *input_file;
 	const gchar *screen_file;
-	ExifItem *item = NULL;
+	unsigned char *profile = NULL;
+	guint profile_len;
 
 	if (imd->cm) return FALSE;
 
@@ -380,8 +381,8 @@
 
 	if (imd->color_profile_use_image && exif)
 		{
-		item = exif_get_item(exif, "Exif.Image.InterColorProfile");
-		if (!item)
+		profile = exif_get_color_profile(exif, &profile_len);
+		if (!profile)
 			{
 			gint cs;
 			gchar *interop_index;
@@ -411,19 +412,15 @@
 			}
 		}
 
-	if (item && exif_item_get_format_id(item) == EXIF_FORMAT_UNDEFINED)
+	if (profile)
 		{
-		unsigned char *data;
-		guint data_len;
 		if (debug) printf("Found embedded color profile\n");
 		imd->color_profile_from_image = COLOR_PROFILE_MEM;
 
-		data = (unsigned char *) exif_item_get_data(item, &data_len);
-
 		cm = color_man_new_embedded(run_in_bg ? imd : NULL, NULL,
-					    data, data_len,
+					    profile, profile_len,
 					    screen_type, screen_file);
-		g_free(data);
+		g_free(profile);
 		}
 	else
 		{
@@ -462,7 +459,7 @@
 	if (options->image.exif_rotate_enable ||
 	    (imd->color_profile_enable && imd->color_profile_use_image) )
 		{
-		exif = exif_read_fd(imd->image_fd, (imd->color_profile_enable && imd->color_profile_use_image));
+		exif = exif_read_fd(imd->image_fd);
 		}
 	if (options->image.exif_rotate_enable && exif)
 		{
@@ -731,7 +728,7 @@
 			{
 			ExifData *exif = NULL;
 
-			if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd, TRUE);
+			if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd);
 //			image_post_process_color(imd, imd->prev_color_row, exif, TRUE);
 			exif_free(exif);
 			}
@@ -1315,7 +1312,7 @@
 		{
 		gint orientation;
 
-		exif = exif_read_fd(imd->image_fd, read_exif_for_color_profile);
+		exif = exif_read_fd(imd->image_fd);
 
 		if (exif && read_exif_for_orientation)
 			{
--- a/src/pan-view.c	Sun Apr 20 21:30:36 2008 +0000
+++ b/src/pan-view.c	Sun Apr 20 21:35:03 2008 +0000
@@ -1433,7 +1433,7 @@
 	gint i;
 
 	if (!fd) return;
-	exif = exif_read_fd(fd, FALSE);
+	exif = exif_read_fd(fd);
 	if (!exif) return;
 
 	pan_text_alignment_add(ta, NULL, NULL);