From: John Ellis Date: Tue, 7 Jun 2005 07:55:00 +0000 (+0000) Subject: Tue Jun 7 03:47:03 2005 John Ellis X-Git-Tag: v1.0.0~1750 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=4832ee633bfa2424c503c2d808bafd0c6f34f8d2 Tue Jun 7 03:47:03 2005 John Ellis * filelist.c (filter_add_defaults): Add Nikon file extension for nef. * format_canon.[ch], format_fuji.[ch]: Add comment tile, and description field for MakerNote parser. * format_nikon.[ch]: Add support for jpegs embedded in Nikon nef files. * format_raw.c: Add debug description output and Nikon raw parser hook. ##### Note: GQview CVS on sourceforge is not always up to date, please use ##### ##### an offical release when making enhancements and translation updates. ##### --- diff --git a/ChangeLog b/ChangeLog index b4a16f9d..a1ef4390 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Tue Jun 7 03:47:03 2005 John Ellis + + * filelist.c (filter_add_defaults): Add Nikon file extension for nef. + * format_canon.[ch], format_fuji.[ch]: Add comment tile, and + description field for MakerNote parser. + * format_nikon.[ch]: Add support for jpegs embedded in Nikon nef files. + * format_raw.c: Add debug description output and Nikon raw parser hook. + Sun Jun 5 03:05:39 2005 John Ellis * filelist.c (path_list_recursive_append): Fix memory leak by using diff --git a/TODO b/TODO index c752b742..5e9d9345 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,28 @@ Major: > cache-load.c: > should honor enable_thumbnails setting + --- + + >raw + exif formats: + + > rethink raw format header parser, apparently canon and nikon both use the TIFF file format, + so it is possible that the same magic header can be in both formats - it only works now + because Canon header list ignores Motorola alignment, which is what Nikon uses. Additionally + matching CRW format uses a magic "HEADCCDR" offset 6 bytes into the file, first two bytes are + similar to tiff for specifying byte alignment (II or MM), so the current code will also pick + up tiff files. Whatever happens here, we want to avoid mmap'ing the file until we are sure. + + > make a generic tiff header and directory parser from the nikon parser for use by all raw + parsers that involve tiff. + + > clean up canon parser (canon_read_int can be substituted with, or wrap exif_get_int16/32). + + > support olympus MakerNote, investigate RAW + > support konica / minolta MakerNote, investigate RAW + + > exif.c parser should not be using EXIF tags during tiff directory search for EXIF tag. + + --- > work on pan view: > Pick a better keyboard shortcut than Control + J :) @@ -57,6 +79,8 @@ Major: > under consideration: > split view + --- + d> fix window size hints not to use USER_SIZE as we do not use gtk_window_resize to set the hint's attribute, and apparently GTK passes in unitialized values for this case (definite programming error, but also a GTK bug?). @@ -88,9 +112,6 @@ Minor (non blockers): > collection window > search window -d> clean up exif.c to be portable (don't assume sizeof(short)==2 and sizeof(long)==4) - - Wishlist?: ---------------------------------------------- diff --git a/src/filelist.c b/src/filelist.c index 1219b88b..c3bb2359 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -211,8 +211,9 @@ void filter_add_defaults(void) /* These are the raw camera formats with embedded jpeg/exif. * (see format_raw.c) */ - filter_add_if_missing("raf", "Fujifilm raw format", ".raf", TRUE); 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); } static GList *filter_to_list(const gchar *extensions) diff --git a/src/format_canon.c b/src/format_canon.c index bdc632a7..41931065 100644 --- a/src/format_canon.c +++ b/src/format_canon.c @@ -34,6 +34,13 @@ #include "exif.h" +/* + *----------------------------------------------------------------------------- + * Raw (CR2, CRW) embedded jpeg extraction for Canon + *----------------------------------------------------------------------------- + */ + + #if 0 #define CANON_DEBUG #endif diff --git a/src/format_canon.h b/src/format_canon.h index 987de1ed..faaefc92 100644 --- a/src/format_canon.h +++ b/src/format_canon.h @@ -33,7 +33,7 @@ gint format_canon_raw(const void *data, const guint len, gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder byte_order); -#define FORMAT_EXIF_CANON { FORMAT_EXIF_MATCH_MAKE, "Canon", 5, format_canon_makernote } +#define FORMAT_EXIF_CANON { FORMAT_EXIF_MATCH_MAKE, "Canon", 5, "Canon", format_canon_makernote } #endif diff --git a/src/format_fuji.c b/src/format_fuji.c index f2d73c0f..5545d99e 100644 --- a/src/format_fuji.c +++ b/src/format_fuji.c @@ -29,6 +29,13 @@ #include "exif.h" +/* + *----------------------------------------------------------------------------- + * Raw (RAF) embedded jpeg extraction for Fujifilm + *----------------------------------------------------------------------------- + */ + + gint format_fuji_raw(const void *data, const guint len, guint *image_offset, guint *exif_offset) { diff --git a/src/format_fuji.h b/src/format_fuji.h index 467a6898..c643f3e2 100644 --- a/src/format_fuji.h +++ b/src/format_fuji.h @@ -27,7 +27,7 @@ gint format_fuji_raw(const void *data, const guint len, gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder byte_order); -#define FORMAT_EXIF_FUJI { FORMAT_EXIF_MATCH_MAKERNOTE, "FUJIFILM", 8, format_fuji_makernote } +#define FORMAT_EXIF_FUJI { FORMAT_EXIF_MATCH_MAKERNOTE, "FUJIFILM", 8, "Fujifilm", format_fuji_makernote } diff --git a/src/format_nikon.c b/src/format_nikon.c index 60ec3cc2..8cc2632c 100644 --- a/src/format_nikon.c +++ b/src/format_nikon.c @@ -2,6 +2,10 @@ * GQView * (C) 2005 John Ellis * + * Authors: + * Raw NEF jpeg extraction based on nefextract.c by Joseph Heled, + * in addition nefextract.c is based on dcraw by Dave Coffin. + * * This software is released under the GNU General Public License (GNU GPL). * Please read the included file COPYING for more information. * This software comes with no warranty of any kind, use at your own risk! @@ -25,6 +29,188 @@ #include "exif.h" +/* + *----------------------------------------------------------------------------- + * Raw NEF embedded jpeg extraction for Nikon + *----------------------------------------------------------------------------- + */ + +static guint tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order, + guint *image_offset, guint *jpeg_len); + + +static void tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order, + guint *image_offset, guint *image_length, guint *jpeg_start, guint *jpeg_len) +{ + static gint size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + guint tag; + guint type; + guint count; + guint segment; + + tag = exif_byte_get_int16(data + offset, byte_order); + type = exif_byte_get_int16(data + offset + 2, byte_order); + count = exif_byte_get_int32(data + offset + 4, byte_order); + + if (type > 12) return; + if (count * size[type] > 4) + { + segment = exif_byte_get_int32(data + offset + 8, byte_order); + if (len < segment + count * size[type]) return; + } + else + { + segment = offset + 8; + } + + if (tag == 0x14a && + type == EXIF_FORMAT_LONG_UNSIGNED) + { + /* sub IFD table */ + gint i; + + for (i = 0; i < count; i++) + { + guint subset; + + subset = exif_byte_get_int32(data + segment + i * 4, byte_order); + tiff_table(data, len, subset, byte_order, image_offset, image_length); + } + + } + else if (tag == 0x201) + { + /* jpeg data start offset */ + *jpeg_start = exif_byte_get_int32(data + segment, byte_order); + } + else if (tag == 0x202) + { + /* jpeg data length */ + *jpeg_len = exif_byte_get_int32(data + segment, byte_order); + } +} + +static guint tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order, + guint *image_offset, guint *image_length) +{ + guint count; + guint i; + guint jpeg_start = 0; + guint jpeg_len = 0; + + if (len < offset + 2) return FALSE; + + count = exif_byte_get_int16((unsigned char *)data + offset, byte_order); + + if (len < offset + count * 12 + 4) return 0; + offset += 2; + + for (i = 0; i < count; i++) + { + tiff_entry(data, len, offset + i * 12, byte_order, + image_offset, image_length, &jpeg_start, &jpeg_len); + } + + if (jpeg_start > 0 && + jpeg_len > *image_length) + { + *image_offset = jpeg_start; + *image_length = jpeg_len; + } + + return exif_byte_get_int32((unsigned char *)data + offset + count * 12, byte_order); +} + +/* + * Walk the first TIFF IFD table and check for existence of a "make" tag (0x10f) that + * identifies NIKON CORPORATION, so that we can abort quickly if it is not a raw NEF. + */ +static gint tiff_nikon_verify(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order) +{ + guint nb_entries; + guint i; + + if (len < offset + 2) return FALSE; + + nb_entries = exif_byte_get_int16(data + offset, byte_order); + offset += 2; + if (len < offset + nb_entries * 12 + 4) return FALSE; + + for (i = 0; i < nb_entries; i++) + { + guint segment; + + segment = offset + i * 12; + if (exif_byte_get_int16(data + segment, byte_order) == 0x10f && + exif_byte_get_int16(data + segment + 2, byte_order) == EXIF_FORMAT_STRING) + { + guint count; + guint make_text; + + count = exif_byte_get_int32(data + segment + 4, byte_order); + make_text = exif_byte_get_int32(data + segment + 8, byte_order); + + if (count >= 17 && + memcmp(data + make_text, "NIKON CORPORATION", 17) == 0) + { + return TRUE; + } + + return FALSE; + } + } + + return FALSE; +} + +gint format_nikon_raw(const void *data, const guint len, + guint *image_offset, guint *exif_offset) +{ + guint i_off = 0; + guint i_len = 0; + ExifByteOrder byte_order; + guint offset; + + if (len < 8) return FALSE; + + if (memcmp(data, "II", 2) == 0) + { + byte_order = EXIF_BYTE_ORDER_INTEL; + } + else if (memcmp(data, "MM", 2) == 0) + { + byte_order = EXIF_BYTE_ORDER_MOTOROLA; + } + else + { + return FALSE; + } + + if (exif_byte_get_int16((unsigned char *)data + 2, byte_order) != 0x002A) + { + return FALSE; + } + + offset = exif_byte_get_int32((unsigned char *)data + 4, byte_order); + if (!tiff_nikon_verify((unsigned char *)data, len, offset, byte_order)) return FALSE; + + while (offset != 0) + { + guint next_offset = 0; + tiff_table((unsigned char *)data, len, offset, byte_order, &i_off, &i_len); + offset = next_offset; + } + + if (i_off != 0) + { + if (image_offset) *image_offset = i_off; + return TRUE; + } + + return FALSE; +} + + /* *----------------------------------------------------------------------------- * EXIF Makernote for Nikon diff --git a/src/format_nikon.h b/src/format_nikon.h index 1988750d..4d543d42 100644 --- a/src/format_nikon.h +++ b/src/format_nikon.h @@ -13,12 +13,18 @@ #include "exif.h" +gint format_nikon_raw(const void *data, const guint len, + guint *image_offset, guint *exif_offset); + +#define FORMAT_RAW_NIKON { "II\x2a\x00", 4, "Nikon tiff raw", format_nikon_raw }, \ + { "MM\x00\x2a", 4, "Nikon tiff raw", format_nikon_raw } + gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder byte_order); -#define FORMAT_EXIF_NIKON { FORMAT_EXIF_MATCH_MAKERNOTE, "Nikon\x00", 6, format_nikon_makernote }, \ - { FORMAT_EXIF_MATCH_MAKE, "NIKON", 5, format_nikon_makernote } +#define FORMAT_EXIF_NIKON { FORMAT_EXIF_MATCH_MAKERNOTE, "Nikon\x00", 6, "Nikon", format_nikon_makernote }, \ + { FORMAT_EXIF_MATCH_MAKE, "NIKON", 5, "Nikon", format_nikon_makernote } #endif diff --git a/src/format_raw.c b/src/format_raw.c index 86c6dd99..37ae03cd 100644 --- a/src/format_raw.c +++ b/src/format_raw.c @@ -33,6 +33,10 @@ #include "format_nikon.h" +/* so that debugging is honored */ +extern gint debug; + + typedef struct _FormatRawEntry FormatRawEntry; struct _FormatRawEntry { const void *header_pattern; @@ -44,6 +48,7 @@ struct _FormatRawEntry { static FormatRawEntry format_raw_list[] = { FORMAT_RAW_CANON, FORMAT_RAW_FUJI, + FORMAT_RAW_NIKON, { NULL, 0, NULL, NULL } }; @@ -53,6 +58,7 @@ struct _FormatExifEntry { FormatExifMatchType header_type; const void *header_pattern; const guint header_length; + const gchar *description; FormatExifParseFunc func_parse; }; @@ -92,6 +98,8 @@ static gint format_raw_parse(FormatRawEntry *entry, if (!entry || !entry->func_parse) return FALSE; + if (debug) printf("RAW using file parser for %s\n", entry->description); + found = entry->func_parse(data, len, &io, &eo); if (!found || @@ -219,6 +227,8 @@ gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offs if (!entry || !entry->func_parse) return FALSE; + if (debug) printf("EXIF using makernote parser for %s\n", entry->description); + return entry->func_parse(exif, tiff, offset, size, byte_order); }