ExifData *exif;
gint i;
- exif = exif_read_fd(eb->fd, FALSE);
+ exif = exif_read_fd(eb->fd);
if (!exif)
{
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;
{
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);
time_t date = -1;
ExifData *exif;
- exif = exif_read_fd(cl->fd, FALSE);
+ exif = exif_read_fd(cl->fd);
if (exif)
{
gchar *text;
{
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;
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
}
return NULL;
}
-ExifData *exif_read_fd(FileData *fd, gint parse_color_profile)
+ExifData *exif_read_fd(FileData *fd)
{
GList *work;
gchar *sidecar_path = NULL;
// 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;
}
return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list);
}
+
/*
*-------------------------------------------------------------------
* jpeg marker utils
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;
- }
-
- 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,
+ 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);
-
- return TRUE;
- }
+ 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 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;
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;
}
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;
+}
+
/*
*-------------------------------------------------------------------
return NULL;
}
-
-
static gint map_file(const gchar *path, void **mapping, int *size)
{
int fd;
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;
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);
}
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:
*-----------------------------------------------------------------------------
*/
-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);
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
#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();
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();
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";
}
}
+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;
+}
+
}
new = g_string_new(str);
- exif = exif_read_fd(imd->image_fd, FALSE);
+ exif = exif_read_fd(imd->image_fd);
prev = 0;
last = FALSE;
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;
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;
}
}
- 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
{
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)
{
{
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);
}
{
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)
{
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);