Mercurial > geeqie.yaz
diff src/format_canon.c @ 45:7cfa60beda76
Thu May 26 13:57:19 2005 John Ellis <johne@verizon.net>
* format_raw.[ch]: Move camera specific code to manufacturer specific
format_*.c files. Change code so that file descripter version is now a
separate functions that wraps the standard parser by using mmap.
* format_canon.[ch]: Moved Canon specific raw support here, removed
file descriptor versions of parser. This Canon raw file parser written
by Daniel M. German.
* format_fuji.[ch]: Move Fuji specific raw support here, parser written
by Lars Ellenberg.
* exif.c: Update for change to format_raw_img_exif_offsets.
* filelist.c: Add cr2 extension to Canon raw format list.
* image-load.c: Fixes for changes to format_raw_img_exif_offset_fd so
that buffer is refilled using new offset of file descriptor.
* src/Makefile.am: Add format_canon.[ch], format_fuji.[ch] to build.
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
##### an offical release when making enhancements and translation updates. #####
author | gqview |
---|---|
date | Thu, 26 May 2005 18:10:52 +0000 |
parents | |
children | aa4c0e1b54b0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/format_canon.c Thu May 26 18:10:52 2005 +0000 @@ -0,0 +1,473 @@ +/* + * GQView + * (C) 2005 John Ellis + * + * 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! + * + * + * Code to add support for Canon CR2 and CRW files, version 0.2 + * + * Developed by Daniel M. German, dmgerman at uvic.ca + * + * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/ + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> + +#include "intl.h" + +#include "format_canon.h" +#include "format_raw.h" + + +#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__)) + +#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. + +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 + + +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; + + 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; + + 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(*(guint32*)(data + *offset)); + *offset +=2; + DEBUG_3("Read 2 bytes %d %x", *value, *value); + } + + DEBUG_EXIT("canon_read_int"); + + DEBUG_ENABLE; + 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) +{ + 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 ). + + */ + + 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; + } + + /* 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; + + +} + + +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)); + + + 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; +} + + +gint format_raw_test_canon(const void *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. + + 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]; + */ + + 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; + + if (format_raw_test_canon_cr2((void *)data, len, image_offset, exif_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))); + + /* 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; + + } + + /* 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 + + +-----------------+ + | data raw | + +-----------------+ + | data jpeg | + +-----------------+ + | data photo info | + +-----------------+ + |header of heap | + | # records | it should be 3 + | raw info | + | jpeg info | + | photo info | + +-----------------+ + + 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 + + */ + + + if (len < 0x10000) { + DEBUG_2("We have a problem, the length is too small %d ", len); + goto return_only; + } + currentOffset = len-4; + + + /* 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; + + 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; + + 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!!!!!!!!!!!!!!!!!!"); + + + /* At this point we are returning */ +return_only: + if (returnValue) { + DEBUG_1("****We got an embedded JPEG for a canon CRW"); + + } + + DEBUG_EXIT("format_raw_test_canon"); + return returnValue; + +#undef DEBUG_2 +#undef DEBUG +#undef DEBUG_ENTRY +#undef DEBUG_EXIT + +} + +