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(&currentOffset, 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(&currentOffset, 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(&currentOffset, 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(&currentOffset, data, 4, &recordLength))
+      goto return_only;
+    
+    DEBUG_2("record length %d ", recordLength);
+    
+    /* Read offset */
+    
+    if (!canon_read_int(&currentOffset, 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
+
+}
+
+