Mercurial > geeqie
changeset 59:57f6da2510d9
Sun Jun 12 19:25:26 2005 John Ellis <johne@verizon.net>
* format_canon.[ch]: Reimplement canon raw parser to use convenience
functions from exif.c, also separated parsers into one per file type.
For the cr2 format also verify compression type in tiff field 0x0103.
* format_raw.c: Add FIXME comment noting current shortcomings.
author | gqview |
---|---|
date | Sun, 12 Jun 2005 23:45:45 +0000 |
parents | df73b94154e4 |
children | 9c0c402b0ef3 |
files | ChangeLog TODO src/format_canon.c src/format_canon.h src/format_raw.c |
diffstat | 5 files changed, 180 insertions(+), 413 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sat Jun 11 05:11:39 2005 +0000 +++ b/ChangeLog Sun Jun 12 23:45:45 2005 +0000 @@ -1,3 +1,10 @@ +Sun Jun 12 19:25:26 2005 John Ellis <johne@verizon.net> + + * format_canon.[ch]: Reimplement canon raw parser to use convenience + functions from exif.c, also separated parsers into one per file type. + For the cr2 format also verify compression type in tiff field 0x0103. + * format_raw.c: Add FIXME comment noting current shortcomings. + Sat Jun 11 01:06:49 2005 John Ellis <johne@verizon.net> * exif.c, format_nikon.c, format_olympus.c: Fix memory leaks,
--- a/TODO Sat Jun 11 05:11:39 2005 +0000 +++ b/TODO Sun Jun 12 23:45:45 2005 +0000 @@ -29,9 +29,9 @@ d> 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 (there are now many convenience utils to simplify tiff header, etc.): - > canon_read_int can be substituted with, or wrap exif_get_int16/32. - > CR2 tiff code can now use exif_tiff_directory_offset. + d> clean up canon parser (there are now many convenience utils to simplify tiff header, etc.): + d> canon_read_int can be substituted with, or wrap exif_get_int16/32. + d> CR2 tiff code can now use exif_tiff_directory_offset. d> support olympus MakerNote, investigate RAW (raw embedded jpeg appears to be tiny). > support konica / minolta MakerNote, investigate RAW.
--- a/src/format_canon.c Sat Jun 11 05:11:39 2005 +0000 +++ b/src/format_canon.c Sun Jun 12 23:45:45 2005 +0000 @@ -40,444 +40,198 @@ *----------------------------------------------------------------------------- */ - -#if 0 - #define CANON_DEBUG -#endif - -#ifdef CANON_DEBUG -int canonEnableDebug = 0; -/* This should be really a stack, but I am too lazy to implement */ -#define DEBUG_ENABLE (canonEnableDebug = 0) -#define DEBUG_DISABLE (canonEnableDebug = 1) -/* It would be nice if these functions indented according to depth in the stack, but I am too lazy to implement */ - -#define DEBUG_ENTRY(a) (canonEnableDebug || fprintf(stderr, "Entering function: %s [%s:%d]\n", a, __FILE__, __LINE__)) -#define DEBUG_EXIT(a) (canonEnableDebug || fprintf(stderr, "Exiting function: %s [%s:%d]\n", a, __FILE__, __LINE__)) -#define DEBUG_1(a) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n", __FILE__, __LINE__)) -#define DEBUG_2(a,b) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b, __FILE__, __LINE__)) -#define DEBUG_3(a,b,c) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b, c, __FILE__, __LINE__)) +static gint canon_cr2_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo, + guint *image_offset, gint *jpeg_encoding) +{ + guint tag; + guint type; + guint count; + guint jpeg_start; -#else -#define DEBUG_ENABLE -#define DEBUG_DISABLE -#define DEBUG_ENTRY(a) -#define DEBUG_EXIT(a) - -#define DEBUG_1(a) -#define DEBUG_2(a,b) -#define DEBUG_3(a,b,c) -#endif - - -/* canon_read_int4 - - -The problem with gqview is that sometimes the data is to be read from -a file, and sometimes it is in memory. This function tries to isolate -the rest of the code from having to deal with both cases - -This function reads a 4 byte unsigned integer, and fixes its endianism. + /* the two (tiff compliant) tags we want are: + * 0x0103 image compression type (must be type 6 for jpeg) + * 0x0111 jpeg start offset + * only use the first segment that contains an actual jpeg - as there + * is a another that contains the raw data. + */ + 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); -If fd >= 0 then the value is read from the corresponding file descriptor - - in that case, if offset is > 0, then the value is read from that offset - - otherwise it is read from the current file pointer - -if fd < 0 then the value is read from the memory pointed by data + offset - + /* tag 0x0103 contains the compression type for this segment's image data */ + if (tag == 0x0103) + { + if (ExifFormatList[type].size * count == 2 && + exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_DATA, bo) == 6) + { + *jpeg_encoding = TRUE; + } + return FALSE; + } -offset is a pointer to the actual offset of the file. - -sizeInt can be 2 or 4 (it is the number of bytes to read) - -RETURNS true is no error, false if it can't read the value - - -*/ -static int canon_read_int(unsigned int *offset, const void *data, int sizeInt, unsigned int *value ) -{ - DEBUG_DISABLE; + /* find and verify jpeg offset */ + if (tag != 0x0111 || + !jpeg_encoding) return FALSE; - DEBUG_ENTRY("canon_read_int"); - /* Verify values before we do anything */ - if (sizeInt != 2 && sizeInt != 4) return FALSE; - if (offset == NULL) return FALSE; - if (*offset <= 0) return FALSE; - if (data == NULL) return FALSE; - if (value == NULL) return FALSE; + /* make sure data segment contains 4 bytes */ + if (ExifFormatList[type].size * count != 4) return FALSE; + + jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo); - if (sizeInt == 4) { - *value = GUINT32_FROM_LE(*(guint32*)(data + *offset)); - *offset +=4; - DEBUG_3("Read 4 bytes %d %x", *value, *value); - } else { - *value = GUINT16_FROM_LE(*(guint16*)(data + *offset)); - *offset +=2; - DEBUG_3("Read 2 bytes %d %x", *value, *value); - } + /* verify this is jpeg data */ + if (len < jpeg_start + 4 || + memcmp(data + jpeg_start, "\xff\xd8", 2) != 0) + { + return FALSE; + } - DEBUG_EXIT("canon_read_int"); - - DEBUG_ENABLE; - return TRUE; + *image_offset = jpeg_start; + return TRUE; } -#define CANON_HEADER_SIZE 26 - -/* - - The CR2 format is really a TIFF format. It is nicely documented in the TIFF V 6.0 document available from adobe. - - The CR2 file contains two thumbnails, one tiny and one decent sized. The record Id of the latter is 0x0111. - - The photo info is also available, in EXIF, and it looks like I don't need to do anything! Yeah! - -*/ - -static int canon_cr2_process_directory(void *data, int offsetIFD, guint *jpegLocation, guint *exifLocation) +static gint canon_cr2_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo, + guint *image_offset) { - unsigned int offset; - int returnValue = FALSE; - - DEBUG_ENTRY("canon_cr2_process_directory"); - - /* The directory is a link list, after an array of records, the next 4 byptes point to the offset of the next directory. - - All offsets are absolution within the file (in CRWs the offsets are relative ). - - */ + gint jpeg_encoding = FALSE; + guint count; + guint i; - while (offsetIFD != 0 && offsetIFD != 0xFFFF) { - int countEntries=0; - int i; - /* Read directory, we start by reading number of entries in the directory */ - - offset = offsetIFD; - if (!canon_read_int(&offset, data, 2, &countEntries)) { - goto return_only; - } - DEBUG_2("Number of entries: %d\n", countEntries); - - for (i=0;i<countEntries;i++) { - /* read each entry */ - - int recordId; -#if 0 - int format; - int size; -#endif - - /* read record type */ - if (!canon_read_int(&offset, data, 2, &recordId)) { - goto return_only; - } + if (len < offset + 2) return 0; - /* Did we find the JPEG */ - if (recordId == 0x0111) { - DEBUG_1("This is the record to find**********************\n"); - offset +=6; - if (!canon_read_int(&offset, data, 4, jpegLocation)) { - goto return_only; - } - DEBUG_3("JPEG Location %d 0x%x\n", *jpegLocation, *jpegLocation); - /* We don't want to keep reading, because there is another - 0x0111 record at the end that contains the raw data */ - returnValue = TRUE; - goto return_only; - } else { - /* advance pointer by skipping rest of record */ - offset += 10; - } - } - /* The next 4 bytes are the offset of next directory, if zero we are done - - */ - if (!canon_read_int(&offset, data, 4, &offsetIFD)) { - goto return_only; - } - DEBUG_3("Value of NEXT offsetIFD: %d 0x%x\n", offsetIFD, offsetIFD); - } - - returnValue = TRUE; - DEBUG_1("Going to return true"); - - return_only: - DEBUG_EXIT("canon_cr2_process_directory"); - - return TRUE; - - -} - + count = exif_byte_get_int16(data + offset, bo); + offset += 2; + if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0; -static int format_raw_test_canon_cr2(void *data, const guint len, - guint *image_offset, guint *exif_offset) -{ -#if 0 - char signature[4]; - unsigned int offset = 4; -#endif - int offsetIFD; - int returnValue = FALSE; - void *jpgInDataOffset; - - DEBUG_ENTRY("format_raw_test_canon_cr2"); - - /* Verify signature */ - if (memcmp(data, "\x49\x49\x2a\00", 4) != 0) { - DEBUG_1("This is not a CR2"); - goto return_only; - } - - /* Get address of first directory */ - offsetIFD = GUINT32_FROM_LE(*(guint32*)(data + 4)); + for (i = 0; i < count; i++) + { + if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo, + image_offset, &jpeg_encoding)) + { + return 0; + } + } - - DEBUG_2("Value of offsetIFD: %d\n", offsetIFD); - - returnValue = canon_cr2_process_directory(data, offsetIFD, image_offset, exif_offset); - - if (returnValue) { - jpgInDataOffset = data + *image_offset; - - /* Make sure we really got a JPEG */ - - if (memcmp(jpgInDataOffset, "\xff\xd8",2) != 0) { - /* It is not at the JPEG! */ - DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset); - returnValue = FALSE; - } - } - -return_only: - DEBUG_EXIT("format_raw_test_canon_cr2"); - - return returnValue; + return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo); } - -gint format_canon_raw(unsigned char *data, const guint len, - guint *image_offset, guint *exif_offset) +gint format_canon_raw_cr2(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset) { - - - /* There are at least 2 types of Canon raw files. CRW and CR2 - - CRW files have a proprietary format. + guint jpeg_offset = 0; + ExifByteOrder bo; + guint offset; + gint level; - HEADER - Heap - RAW data - JPEG data - PHoto data - - HEADER_LENGTH 32 bytes - int2 byteOrder; Always II (MM Motorola ---big endian, II Intel --little endian) - int4 length; Should be 26 - char identifier[8];type HEAP, subtype heap CCDR - int2 version; - int2 subversion; - char unused[14]; - */ + /* cr2 files are tiff files with a few canon specific directory tags + * they are (always ?) in little endian format + */ + if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE; - int returnValue = FALSE; - int heapHeaderOffset = 0; - int heapRecordsCount = 0; -#if 0 - guint32 rawInt4; - guint16 rawInt2; -#endif - int i; - unsigned int currentOffset; - /* File has to be little endian, first two bytes II */ - - if (len < 100) - return FALSE; + level = 0; + while (offset && level < EXIF_TIFF_MAX_LEVELS) + { + offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset); + level++; - if (format_raw_test_canon_cr2((void *)data, len, image_offset, exif_offset)) { - return TRUE; - } + if (jpeg_offset != 0) + { + if (image_offset) *image_offset = jpeg_offset; + return TRUE; + } + } - if (memcmp("II", data, 2) != 0) { - return FALSE; - } - /* NO DEBUG BEFORE THIS POINT, we want to debug only Canon */ - - DEBUG_ENTRY("format_raw_test_canon"); - - DEBUG_2("Length of buffer read %u", len); - - DEBUG_2("CRW header length Data %d", GUINT32_FROM_LE(*(guint32*)(data + 2))); + return FALSE; +} - /* the length has to be CANON_HEADER_SIZE */ - if (GUINT32_FROM_LE(*(guint32*)(data + 2)) != CANON_HEADER_SIZE) { - DEBUG_1("It is not the right size"); - goto return_only; - } - - if (!memcmp("HEAPCCDR", data+6, 8) == 0) { - DEBUG_1("This file is not a Canon CRW raw photo"); - goto return_only; +#define CRW_BYTE_ORDER EXIF_BYTE_ORDER_INTEL +#define CRW_HEADER_SIZE 26 +#define CRW_DIR_ENTRY_SIZE 10 - } - - /* Ok, so now we know that this is a CRW file */ - - /* The heap is a strange data structure. It is recursive, so a record - can contain a heap itself. That is indeed the case for the photo information - reecord. Luckily the first heap contains the jpeg, so we don't need to do - any recursive processing. - - Its "header" is a the end. The header is a sequence of records, - and the data of each record is at the beginning of the heap +gint format_canon_raw_crw(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset) +{ + guint block_offset; + guint data_length; + guint offset; + guint count; + guint i; - +-----------------+ - | data raw | - +-----------------+ - | data jpeg | - +-----------------+ - | data photo info | - +-----------------+ - |header of heap | - | # records | it should be 3 - | raw info | - | jpeg info | - | photo info | - +-----------------+ + /* CRW header starts with 2 bytes for byte order (always "II", little endian), + * 4 bytes for start of root block, + * and 8 bytes of magic for file type and format "HEAPCCDR" + * (also 4 bytes for file version, and 8 bytes reserved) + * + * CIFF specification in pdf format is available on some websites, + * search for "CIFFspecV1R03.pdf" or "CIFFspecV1R04.pdf" + */ + if (len < CRW_HEADER_SIZE || + memcmp(data, "II", 2) != 0 || + memcmp(data + 6, "HEAPCCDR", 8) != 0) + { + return FALSE; + } - The header contains - number of records: 2 bytes - for each record (10 bytes long) - type: 2 bytes - length: 4 bytes - offset: 4 bytes - - In some records the length and offset are actually data, - but none for the ones in the first heap. - - the offset is with respect to the beginning of the heap, not the - beginning of the file. That allows heaps to be "movable" - - For the purpose of finding the JPEG, all we need is to scan the fist heap, - which contains the following record types: - - 0x2005 Record RAW data - 0x2007 Record JPEG data - 0x300a Record with photo info + block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER); - */ - + /* the end of the root block equals end of file, + * the last 4 bytes of the root block contain the block's data size + */ + offset = len - 4; + data_length = exif_byte_get_int32(data + offset, CRW_BYTE_ORDER); - if (len < 0x10000) { - DEBUG_2("We have a problem, the length is too small %d ", len); - goto return_only; - } - currentOffset = len-4; - + offset = block_offset + data_length; + if (len < offset + 2) return FALSE; - /* The last 4 bytes have the offset of the header of the heap */ - if (!canon_read_int(¤tOffset, data, 4, &heapHeaderOffset)) - goto return_only; - - /* The heapoffset has to be adjusted to the actual file size, the header is CANON_HEADER_SIZE bytes long */ - heapHeaderOffset += CANON_HEADER_SIZE; - DEBUG_2("heap header Offset %d ", heapHeaderOffset); - - /* Just check, it does not hurt, we don't want to crash */ - if (heapHeaderOffset > len) - goto return_only; + /* number of directory entries for this block is in + * the next two bytes after the data for this block. + */ + count = exif_byte_get_int16(data + offset, CRW_BYTE_ORDER); + offset += 2; + if (len < offset + count * CRW_DIR_ENTRY_SIZE + 4) return FALSE; + + /* walk the directory entries looking for type jpeg (tag 0x2007), + * for reference, other tags are 0x2005 for raw and 0x300a for photo info: + */ + for (i = 0; i < count ; i++) + { + guint entry_offset; + guint record_type; + guint record_offset; + guint record_length; + + entry_offset = offset + i * CRW_DIR_ENTRY_SIZE; - currentOffset = heapHeaderOffset; - /* Let us read the number of records in the heap */ - if (!canon_read_int(¤tOffset, data, 2, &heapRecordsCount)) - goto return_only; - - DEBUG_2("heap record count %d ", heapRecordsCount); - - if (heapRecordsCount != 3) { - /* In all the cameras I have seen, this is always 3 - if not, something is wrong, so just quit */ - goto return_only; - } - - for (i=0;i<3;i++) { - int recordType; - int recordOffset; - int recordLength; - const void *jpgInDataOffset; - /* Read each record, to find jpg, it should be second */ - - if (!canon_read_int(¤tOffset, data, 2, &recordType)) - goto return_only; - - DEBUG_2("record type 0x%x ", recordType); - - if (recordType != 0x2007) { - /* Go to the next record, don't waste time, - but first, eat 8 bytes from header */ - currentOffset += 8; - continue; /* Nah, wrong record, go to next */ - } - /* Bingo, we are at the JPEG record */ - - /* Read length */ - if (!canon_read_int(¤tOffset, data, 4, &recordLength)) - goto return_only; - - DEBUG_2("record length %d ", recordLength); - - /* Read offset */ - - if (!canon_read_int(¤tOffset, data, 4, &recordOffset)) - goto return_only; - - DEBUG_2("record offset 0x%d ", recordOffset); - - /* Great, we now know where the JPEG is! - it is CANON_HEADER_SIZE (size of CRW header) + recordOffset - */ - - *image_offset = CANON_HEADER_SIZE + recordOffset; - DEBUG_2("image offset %d ", *image_offset); - - /* keep checking for potential errors */ - if (*image_offset > len) { - goto return_only; - } - /* Get the JPEG is */ - - jpgInDataOffset = data + *image_offset; + /* entry is 10 bytes (in order): + * 2 for type + * 4 for length of data + * 4 for offset into data segment of this block + */ + record_type = exif_byte_get_int16(data + entry_offset, CRW_BYTE_ORDER); + record_length = exif_byte_get_int32(data + entry_offset + 2, CRW_BYTE_ORDER); + record_offset = exif_byte_get_int32(data + entry_offset + 6, CRW_BYTE_ORDER); + + /* tag we want for jpeg data */ + if (record_type == 0x2007) + { + guint jpeg_offset; - if (memcmp(jpgInDataOffset, "\xff\xd8\xff\xdb",4) != 0) { - /* It is not at the JPEG! */ - DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset); - goto return_only; - } - returnValue = TRUE; - goto return_only; - } - /* undo whatever we need in case of an error*/ - DEBUG_1("We scan all records, but nothing was found!!!!!!!!!!!!!!!!!!"); - + jpeg_offset = block_offset + record_offset; + if (len < jpeg_offset + record_length || + record_length < 4 || + memcmp(data + jpeg_offset, "\xff\xd8\xff\xdb", 4) != 0) + { + return FALSE; + } - /* At this point we are returning */ -return_only: - if (returnValue) { - DEBUG_1("****We got an embedded JPEG for a canon CRW"); - - } + /* we now know offset and verified jpeg */ + *image_offset = jpeg_offset; + return TRUE; + } + } - DEBUG_EXIT("format_raw_test_canon"); - return returnValue; + return FALSE; +} -#undef DEBUG_2 -#undef DEBUG -#undef DEBUG_ENTRY -#undef DEBUG_EXIT - -} /* *-----------------------------------------------------------------------------
--- a/src/format_canon.h Sat Jun 11 05:11:39 2005 +0000 +++ b/src/format_canon.h Sun Jun 12 23:45:45 2005 +0000 @@ -22,16 +22,18 @@ #include "exif.h" -gint format_canon_raw(unsigned char *data, const guint len, - guint *image_offset, guint *exif_offset); +gint format_canon_raw_crw(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset); +gint format_canon_raw_cr2(unsigned char *data, const guint len, + guint *image_offset, guint *exif_offset); #define FORMAT_RAW_CANON { "crw", \ FORMAT_RAW_MATCH_MAGIC, 6, "HEAPCCDR", 8, \ - "Canon crw", format_canon_raw }, \ + "Canon crw", format_canon_raw_crw }, \ { "cr2", \ FORMAT_RAW_MATCH_TIFF_MAKE, 0, "Canon", 5, \ - "Canon cr2", format_canon_raw } + "Canon cr2", format_canon_raw_cr2 } gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
--- a/src/format_raw.c Sat Jun 11 05:11:39 2005 +0000 +++ b/src/format_raw.c Sun Jun 12 23:45:45 2005 +0000 @@ -304,6 +304,10 @@ if (debug) printf("RAW file parser extension match\n"); } + /* FIXME: + * when the target is a tiff file it should be mmaped prior to format_raw_find as + * the make field data may not always be within header_data + header_len + */ entry = format_raw_find(header_data, header_len); if (!entry || !entry->func_parse) return FALSE;