*-----------------------------------------------------------------------------
*/
-
-#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 )
+static gint canon_cr2_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
+ guint *image_offset, gint *jpeg_encoding)
{
- 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(*(guint16*)(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
+ guint tag;
+ guint type;
+ guint count;
+ guint jpeg_start;
+
+ /* 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);
+
+ /* 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;
+ }
-/*
+ /* find and verify jpeg offset */
+ if (tag != 0x0111 ||
+ !jpeg_encoding) return FALSE;
- The CR2 format is really a TIFF format. It is nicely documented in the TIFF V 6.0 document available from adobe.
+ /* make sure data segment contains 4 bytes */
+ if (ExifFormatList[type].size * count != 4) return FALSE;
- The CR2 file contains two thumbnails, one tiny and one decent sized. The record Id of the latter is 0x0111.
+ jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
- The photo info is also available, in EXIF, and it looks like I don't need to do anything! Yeah!
+ /* verify this is jpeg data */
+ if (len < jpeg_start + 4 ||
+ memcmp(data + jpeg_start, "\xff\xd8", 2) != 0)
+ {
+ return FALSE;
+ }
-*/
+ *image_offset = jpeg_start;
+ return TRUE;
+}
-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 */
+ if (len < offset + 2) return 0;
- 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;
+ 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++)
+ {
+ if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo,
+ image_offset, &jpeg_encoding))
+ {
+ return 0;
+ }
+ }
+ return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
}
-
-static int format_raw_test_canon_cr2(void *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)
{
-#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;
- }
- }
+ guint jpeg_offset = 0;
+ ExifByteOrder bo;
+ guint offset;
+ gint level;
+
+ /* 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;
+
+ level = 0;
+ while (offset && level < EXIF_TIFF_MAX_LEVELS)
+ {
+ offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset);
+ level++;
-return_only:
- DEBUG_EXIT("format_raw_test_canon_cr2");
+ if (jpeg_offset != 0)
+ {
+ if (image_offset) *image_offset = jpeg_offset;
+ return TRUE;
+ }
+ }
- return returnValue;
+ return FALSE;
}
+#define CRW_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
+#define CRW_HEADER_SIZE 26
+#define CRW_DIR_ENTRY_SIZE 10
-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)
{
+ guint block_offset;
+ guint data_length;
+ guint offset;
+ guint count;
+ guint i;
+
+ /* 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;
+ }
+ block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER);
- /* There are at least 2 types of Canon raw files. CRW and CR2
-
- CRW files have a proprietary format.
+ /* 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);
- HEADER
- Heap
- RAW data
- JPEG data
- PHoto data
+ offset = block_offset + data_length;
+ if (len < offset + 2) return FALSE;
- 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];
- */
+ /* 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;
- 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
+ /* 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;
+
+ /* 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;
+
+ 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;
+ }
+
+ /* we now know offset and verified jpeg */
+ *image_offset = jpeg_offset;
+ return TRUE;
+ }
+ }
+ return FALSE;
}
+
/*
*-----------------------------------------------------------------------------
* EXIF Makernote for Canon