From: John Ellis Date: Tue, 7 Nov 2006 21:00:50 +0000 (+0000) Subject: Tue Nov 7 15:35:59 2006 John Ellis X-Git-Tag: v1.0.0~1702 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=4c75075a036f20d879746f73c994e6e67d464eeb Tue Nov 7 15:35:59 2006 John Ellis * exif.c: Use new format_raw_exif_offset() function to find Exif in raw files. * filelist.c: Add orf and pef to displayed file types. * format_canon.h, format_fuji.h: Update to new #define format. * format_fuji.c: Use same offset for Exif as the jpeg image as the Exif is always embedded in the jpeg and assuming offset of 12 is just broken. * format_nikon.h: Update to new #define format, and add pentax here as finding the jpeg will be same code. * format_olympus.[ch]: Support Olympus raw files with embedded jpegs, not all raw files will have a jpeg, but all appear to have Exif tags. * format_raw.[ch]: Add new camera types, and add a debugging facility to easily list all tags within tiff files (see format_raw.h to enable). --- diff --git a/ChangeLog b/ChangeLog index a8f46079..cecb04c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Tue Nov 7 15:35:59 2006 John Ellis + + * exif.c: Use new format_raw_exif_offset() function to find Exif in + raw files. + * filelist.c: Add orf and pef to displayed file types. + * format_canon.h, format_fuji.h: Update to new #define format. + * format_fuji.c: Use same offset for Exif as the jpeg image as the + Exif is always embedded in the jpeg and assuming offset of 12 is + just broken. + * format_nikon.h: Update to new #define format, and add pentax here + as finding the jpeg will be same code. + * format_olympus.[ch]: Support Olympus raw files with embedded jpegs, + not all raw files will have a jpeg, but all appear to have Exif tags. + * format_raw.[ch]: Add new camera types, and add a debugging facility + to easily list all tags within tiff files (see format_raw.h to enable). + Mon Nov 6 20:53:22 2006 John Ellis * format_olympus.c: Fix olympus makernote tag types to match image diff --git a/TODO b/TODO index be3dbe36..71cf578b 100644 --- a/TODO +++ b/TODO @@ -33,6 +33,11 @@ d> figure out if crash when expanding a folder in the folder tree view when pess > exif.c parser should not be using EXIF tags during tiff directory search for EXIF tag. + > fix parsers to properly indicate which offsets are valid + instead of making one zero if the other is found, this leads to making + the file loader parse an entire file even if we found no jpeg but did find an exif segment. + (simply set non-found offsets to file length?) + --- > work on pan view: diff --git a/src/exif.c b/src/exif.c index 982b4ea4..54b3b811 100644 --- a/src/exif.c +++ b/src/exif.c @@ -1098,11 +1098,37 @@ ExifData *exif_read(const gchar *path) if (res != 0) { + FormatRawExifType exif_type; + FormatRawExifParseFunc exif_parse_func; guint32 offset = 0; - - if (format_raw_img_exif_offsets(f, size, NULL, &offset)) + + exif_type = format_raw_exif_offset(f, size, &offset, &exif_parse_func); + switch (exif_type) { - res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList); + case FORMAT_RAW_EXIF_NONE: + default: + break; + case FORMAT_RAW_EXIF_TIFF: + res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, + ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_JPEG: + res = exif_parse_JPEG(exif, (unsigned char*)f + offset, size - offset, + ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_IFD_II: + case FORMAT_RAW_EXIF_IFD_MM: + res = exif_parse_IFD_table(exif, (unsigned char*)f, offset, size - offset, + (exif_type == FORMAT_RAW_EXIF_IFD_II) ? + EXIF_BYTE_ORDER_INTEL : EXIF_BYTE_ORDER_MOTOROLA, + 0, ExifKnownMarkersList); + break; + case FORMAT_RAW_EXIF_PROPRIETARY: + if (exif_parse_func) + { + res = exif_parse_func((unsigned char*)f + offset, size - offset, exif); + } + break; } } diff --git a/src/filelist.c b/src/filelist.c index 1aa1ecd0..a6e87830 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -215,6 +215,8 @@ void filter_add_defaults(void) filter_add_if_missing("crw", "Canon raw format", ".crw;.cr2", TRUE); filter_add_if_missing("raf", "Fujifilm raw format", ".raf", TRUE); filter_add_if_missing("nef", "Nikon raw format", ".nef", TRUE); + filter_add_if_missing("orf", "Olympus raw format", ".orf", TRUE); + filter_add_if_missing("pef", "Pentax raw format", ".pef", TRUE); } static GList *filter_to_list(const gchar *extensions) diff --git a/src/format_canon.h b/src/format_canon.h index 64f0e1f1..b2422b43 100644 --- a/src/format_canon.h +++ b/src/format_canon.h @@ -30,9 +30,11 @@ gint format_canon_raw_cr2(unsigned char *data, const guint len, #define FORMAT_RAW_CANON { "crw", \ FORMAT_RAW_MATCH_MAGIC, 6, "HEAPCCDR", 8, \ + FORMAT_RAW_EXIF_NONE, NULL, \ "Canon crw", format_canon_raw_crw }, \ { "cr2", \ FORMAT_RAW_MATCH_TIFF_MAKE, 0, "Canon", 5, \ + FORMAT_RAW_EXIF_TIFF, NULL, \ "Canon cr2", format_canon_raw_cr2 } diff --git a/src/format_fuji.c b/src/format_fuji.c index 6eef3666..6bc99f82 100644 --- a/src/format_fuji.c +++ b/src/format_fuji.c @@ -48,8 +48,9 @@ gint format_fuji_raw(unsigned char *data, const guint len, return FALSE; } + /* offset to jpeg is embedded at bytes 84-87 */ io = GUINT32_FROM_BE(*(guint32*)(data + 84)); - eo = *image_offset + 12; + if (io + 4 > len) return FALSE; /* verify jpeg marker */ if (memcmp(data + io, "\xff\xd8\xff\xe1", 4) != 0) @@ -57,11 +58,12 @@ gint format_fuji_raw(unsigned char *data, const guint len, return FALSE; } + /* Exif is stored in the jpeg, so use the same offset */ + eo=io; + if (image_offset) *image_offset = io; if (exif_offset) *exif_offset = eo; - printf("raw Fuji format file\n"); - return TRUE; } diff --git a/src/format_fuji.h b/src/format_fuji.h index 29ff565d..fc283626 100644 --- a/src/format_fuji.h +++ b/src/format_fuji.h @@ -23,6 +23,7 @@ gint format_fuji_raw(unsigned char *data, const guint len, #define FORMAT_RAW_FUJI { "raf", \ FORMAT_RAW_MATCH_MAGIC, 0, "FUJIFILM", 8, \ + FORMAT_RAW_EXIF_JPEG, NULL, \ "Fuji raw", format_fuji_raw } diff --git a/src/format_nikon.h b/src/format_nikon.h index c089cf60..f5882d26 100644 --- a/src/format_nikon.h +++ b/src/format_nikon.h @@ -18,8 +18,17 @@ gint format_nikon_raw(unsigned char *data, const guint len, #define FORMAT_RAW_NIKON { "nef", \ FORMAT_RAW_MATCH_TIFF_MAKE, 0, "NIKON CORPORATION", 17, \ + FORMAT_RAW_EXIF_TIFF, NULL, \ "Nikon raw", format_nikon_raw } +/* If your format is basically just TIFF with an embedded jpeg, + * then avoid duplicating code and just stick it here and use the existing nikon parse. + */ +#define FORMAT_RAW_PENTAX { "pef", \ + FORMAT_RAW_MATCH_MAGIC, 242, "PENTAX Corporation", 18, \ + FORMAT_RAW_EXIF_TIFF, NULL, \ + "Pentax raw", format_nikon_raw } + gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder bo); diff --git a/src/format_olympus.c b/src/format_olympus.c index 0ea27380..46b76f3f 100644 --- a/src/format_olympus.c +++ b/src/format_olympus.c @@ -26,6 +26,115 @@ #include "exif.h" +/* + *----------------------------------------------------------------------------- + * Raw ORF embedded jpeg extraction for Olympus + *----------------------------------------------------------------------------- + */ + +static guint olympus_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo, + gint level, + guint *image_offset, guint *exif_offset); + + +static void olympus_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo, + gint level, + guint *image_offset, guint *exif_offset) +{ + guint tag; + guint type; + guint count; + guint segment; + guint seg_len; + + tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo); + type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo); + count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo); + + /* so far, we only care about tags with type long */ + if (type != EXIF_FORMAT_LONG_UNSIGNED && type != EXIF_FORMAT_LONG) return; + + seg_len = ExifFormatList[type].size * count; + if (seg_len > 4) + { + segment = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo); + if (segment + seg_len > len) return; + } + else + { + segment = offset + EXIF_TIFD_OFFSET_DATA; + } + + if (tag == 0x201) + { + /* start of embedded jpeg, not all olympus cameras embed a jpeg */ + *image_offset = exif_byte_get_int32(data + segment, bo); + } + + if (tag == 0x8769) + { + /* This is the Exif info */ + *exif_offset = exif_byte_get_int32(data + segment, bo); + } +} + +static guint olympus_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo, + gint level, + guint *image_offset, guint *exif_offset) +{ + guint count; + guint i; + + if (level > EXIF_TIFF_MAX_LEVELS) return 0; + + if (len < offset + 2) return FALSE; + + count = exif_byte_get_int16(data + offset, bo); + offset += 2; + if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0; + + for (i = 0; i < count; i++) + { + olympus_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo, level, + image_offset, exif_offset); + } + + return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo); +} + +gint format_olympus_raw(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset) +{ + guint i_off = 0; + guint e_off = 0; + guint offset; + gint level; + + if (len < 8) return FALSE; + + /* these are in tiff file format with a different magick header */ + if (memcmp(data, "IIR", 3) != 0) return FALSE; + + offset = exif_byte_get_int32(data + 4, EXIF_BYTE_ORDER_INTEL); + + level = 0; + while (offset && level < EXIF_TIFF_MAX_LEVELS) + { + offset = olympus_tiff_table(data, len, offset, EXIF_BYTE_ORDER_INTEL, 0, &i_off, &e_off); + level++; + } + + if (i_off != 0 || e_off != 0) + { + if (image_offset) *image_offset = i_off; + if (exif_offset) *exif_offset = e_off; + return TRUE; + } + + return FALSE; +} + + /* *----------------------------------------------------------------------------- * EXIF Makernote for Olympus diff --git a/src/format_olympus.h b/src/format_olympus.h index 7dac58c5..edb70614 100644 --- a/src/format_olympus.h +++ b/src/format_olympus.h @@ -14,15 +14,14 @@ #include "exif.h" -#if 0 gint format_olympus_raw(unsigned char *data, const guint len, guint *image_offset, guint *exif_offset); #define FORMAT_RAW_OLYMPUS { "orf", \ - FORMAT_RAW_MATCH_MAGIC, 0, "IIRS", 4, \ + FORMAT_RAW_MATCH_MAGIC, 0, "IIR", 3, \ + FORMAT_RAW_EXIF_IFD_II, NULL, \ "Olympus raw", format_olympus_raw } -#endif gint format_olympus_makernote(ExifData *exif, unsigned char *tiff, guint offset, diff --git a/src/format_raw.c b/src/format_raw.c index b130e628..47f382fd 100644 --- a/src/format_raw.c +++ b/src/format_raw.c @@ -1,6 +1,6 @@ /* * GQView - * (C) 2005 John Ellis + * (C) 2006 John Ellis * * Authors: * Original version 2005 Lars Ellenberg, base on dcraw by David coffin. @@ -45,15 +45,22 @@ struct _FormatRawEntry { const guint magic_offset; const void *magic_pattern; const guint magic_length; + const FormatRawExifType exif_type; + FormatRawExifParseFunc exif_func; const gchar *description; FormatRawParseFunc func_parse; }; static FormatRawEntry format_raw_list[] = { +#if DEBUG_RAW_TIFF + FORMAT_RAW_DEBUG_TIFF, +#endif FORMAT_RAW_CANON, FORMAT_RAW_FUJI, FORMAT_RAW_NIKON, - { NULL, 0, 0, NULL, 0, NULL, NULL } + FORMAT_RAW_OLYMPUS, + FORMAT_RAW_PENTAX, + { NULL, 0, 0, NULL, 0, 0, NULL, NULL, NULL } }; @@ -265,6 +272,28 @@ gint format_raw_img_exif_offsets(unsigned char *data, const guint len, } +FormatRawExifType format_raw_exif_offset(unsigned char *data, const guint len, guint *exif_offset, + FormatRawExifParseFunc *exif_parse_func) +{ + FormatRawEntry *entry; + + if (!data || len < 1) return FALSE; + + entry = format_raw_find(data, len); + + if (!entry || !entry->func_parse) return FALSE; + + if (!format_raw_parse(entry, data, len, NULL, exif_offset)) return FORMAT_RAW_EXIF_NONE; + + if (entry->exif_type == FORMAT_RAW_EXIF_PROPRIETARY && exif_parse_func) + { + *exif_parse_func = entry->exif_func; + } + + return entry->exif_type; +} + + 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) @@ -399,4 +428,135 @@ gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offs return entry->func_parse(exif, tiff, offset, size, bo); } +/* + *----------------------------------------------------------------------------- + * Basic TIFF debugger, prints all IFD entries within tiff file + *----------------------------------------------------------------------------- + */ +#if DEBUG_RAW_TIFF + +static guint format_debug_tiff_table(unsigned char *data, const guint len, guint offset, + ExifByteOrder bo, gint level); + +static void format_debug_tiff_entry(unsigned char *data, const guint len, guint offset, + ExifByteOrder bo, gint level) +{ + guint tag; + guint type; + guint count; + guint segment; + guint seg_len; + + tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo); + type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo); + count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo); + + seg_len = ExifFormatList[type].size * count; + if (seg_len > 4) + { + segment = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo); + if (segment + seg_len > len) return; + } + else + { + segment = offset + EXIF_TIFD_OFFSET_DATA; + } + + printf("%*stag:0x%04X (%05d), type:%2d %9s, len:%6d [%02X %02X %02X %02X] @ offset:%d\n", + level, "", tag, tag, type, + (type < EXIF_FORMAT_COUNT) ? ExifFormatList[type].short_name : "???", count, + data[segment], data[segment + 1], data[segment + 2], data[segment + 3], segment); + + if (tag == 0x8769 || tag == 0x14a) + { + gint i; + + printf("%*s~~~ found %s table\n", level, "", (tag == 0x14a) ? "subIFD" : "EXIF" ); + + for (i = 0; i < count; i++) + { + guint subset; + + subset = exif_byte_get_int32(data + segment + i * 4, bo); + format_debug_tiff_table(data, len, subset, bo, level + 1); + } + } + else if (tag == 0x201 && (type == EXIF_FORMAT_LONG_UNSIGNED || type == EXIF_FORMAT_LONG)) + { + guint subset = exif_byte_get_int32(data + segment, bo); + printf("%*s~~~ found jpeg data at offset %d\n", level, "", subset); + } + else if (tag == 0x202 && (type == EXIF_FORMAT_LONG_UNSIGNED || type == EXIF_FORMAT_LONG)) + { + guint subset = exif_byte_get_int32(data + segment, bo); + printf("%*s~~~ found jpeg data length of %d\n", level, "", subset); + } +} + +static guint format_debug_tiff_table(unsigned char *data, const guint len, guint offset, + ExifByteOrder bo, gint level) +{ + guint count; + guint i; + + if (level > EXIF_TIFF_MAX_LEVELS) return 0; + + if (len < offset + 2) return FALSE; + + count = exif_byte_get_int16(data + offset, bo); + offset += 2; + if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0; + + printf("%*s== tiff table #%d has %d entries ==\n", level, "", level, count); + + for (i = 0; i < count; i++) + { + format_debug_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo, level); + } + + printf("%*s----------- end of #%d ------------\n", level, "", level); + + return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo); +} + +gint format_debug_tiff_raw(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset) +{ + ExifByteOrder bo; + gint level; + guint offset; + + if (len < 8) return FALSE; + + /* for debugging, we are more relaxed as to magic header */ + if (memcmp(data, "II", 2) == 0) + { + bo = EXIF_BYTE_ORDER_INTEL; + } + else if (memcmp(data, "MM", 2) == 0) + { + bo = EXIF_BYTE_ORDER_MOTOROLA; + } + else + { + return FALSE; + } + + printf("*** debug parsing tiff\n"); + + offset = exif_byte_get_int32(data + 4, bo); + level = 0; + while (offset && level < EXIF_TIFF_MAX_LEVELS) + { + offset = format_debug_tiff_table(data, len, offset, bo, 0); + level++; + } + + printf("*** end\n"); + + /* we are debugging, not trying to return any data */ + return FALSE; +} +#endif + diff --git a/src/format_raw.h b/src/format_raw.h index 8531e014..d46fca79 100644 --- a/src/format_raw.h +++ b/src/format_raw.h @@ -22,15 +22,30 @@ typedef enum { FORMAT_RAW_MATCH_TIFF_MAKE } FormatRawMatchType; +typedef enum { + FORMAT_RAW_EXIF_NONE, + FORMAT_RAW_EXIF_TIFF, + FORMAT_RAW_EXIF_JPEG, + FORMAT_RAW_EXIF_IFD_II, + FORMAT_RAW_EXIF_IFD_MM, + FORMAT_RAW_EXIF_PROPRIETARY +} FormatRawExifType; + typedef gint (* FormatRawParseFunc)(unsigned char *data, const guint len, guint *image_offset, guint *exif_offset); +typedef gint (* FormatRawExifParseFunc)(unsigned char *data, const guint len, + ExifData *exif); + gint format_raw_img_exif_offsets(unsigned char *data, const guint len, guint *image_offset, guint *exif_offset); 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); +FormatRawExifType format_raw_exif_offset(unsigned char *data, const guint len, guint *exif_offset, + FormatRawExifParseFunc *exif_parse_func); + typedef enum { FORMAT_EXIF_MATCH_MAKE, @@ -44,5 +59,23 @@ gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offs guint size, ExifByteOrder bo); +#define DEBUG_RAW_TIFF 0 + +#if DEBUG_RAW_TIFF + +#define FORMAT_RAW_DEBUG_TIFF { "", \ + FORMAT_RAW_MATCH_MAGIC, 0, "II", 2, \ + FORMAT_RAW_EXIF_NONE, NULL, \ + "Tiff debugger II", format_debug_tiff_raw }, \ + { "", \ + FORMAT_RAW_MATCH_MAGIC, 0, "MM", 2, \ + FORMAT_RAW_EXIF_NONE, NULL, \ + "Tiff debugger MM", format_debug_tiff_raw } + +/* used for debugging only */ +gint format_debug_tiff_raw(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset); +#endif + #endif