5 * This software is released under the GNU General Public License (GNU GPL).
6 * Please read the included file COPYING for more information.
7 * This software comes with no warranty of any kind, use at your own risk!
10 * Code to add support for Canon CR2 and CRW files, version 0.2
12 * Developed by Daniel M. German, dmgerman at uvic.ca
14 * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/
32 #include "format_canon.h"
33 #include "format_raw.h"
39 *-----------------------------------------------------------------------------
40 * Raw (CR2, CRW) embedded jpeg extraction for Canon
41 *-----------------------------------------------------------------------------
44 static gint canon_cr2_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
45 guint *image_offset, gint *jpeg_encoding)
52 /* the two (tiff compliant) tags we want are:
53 * 0x0103 image compression type (must be type 6 for jpeg)
54 * 0x0111 jpeg start offset
55 * only use the first segment that contains an actual jpeg - as there
56 * is a another that contains the raw data.
58 tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo);
59 type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
60 count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo);
62 /* tag 0x0103 contains the compression type for this segment's image data */
65 if (ExifFormatList[type].size * count == 2 &&
66 exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_DATA, bo) == 6)
68 *jpeg_encoding = TRUE;
73 /* find and verify jpeg offset */
75 !jpeg_encoding) return FALSE;
77 /* make sure data segment contains 4 bytes */
78 if (ExifFormatList[type].size * count != 4) return FALSE;
80 jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
82 /* verify this is jpeg data */
83 if (len < jpeg_start + 4 ||
84 memcmp(data + jpeg_start, "\xff\xd8", 2) != 0)
89 *image_offset = jpeg_start;
93 static gint canon_cr2_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
96 gint jpeg_encoding = FALSE;
100 if (len < offset + 2) return 0;
102 count = exif_byte_get_int16(data + offset, bo);
104 if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0;
106 for (i = 0; i < count; i++)
108 if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo,
109 image_offset, &jpeg_encoding))
115 return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
118 gint format_canon_raw_cr2(unsigned char *data, const guint len,
119 guint *image_offset, guint *exif_offset)
121 guint jpeg_offset = 0;
126 /* cr2 files are tiff files with a few canon specific directory tags
127 * they are (always ?) in little endian format
129 if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
132 while (offset && level < EXIF_TIFF_MAX_LEVELS)
134 offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset);
137 if (jpeg_offset != 0)
139 if (image_offset) *image_offset = jpeg_offset;
147 #define CRW_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
148 #define CRW_HEADER_SIZE 26
149 #define CRW_DIR_ENTRY_SIZE 10
151 gint format_canon_raw_crw(unsigned char *data, const guint len,
152 guint *image_offset, guint *exif_offset)
160 /* CRW header starts with 2 bytes for byte order (always "II", little endian),
161 * 4 bytes for start of root block,
162 * and 8 bytes of magic for file type and format "HEAPCCDR"
163 * (also 4 bytes for file version, and 8 bytes reserved)
165 * CIFF specification in pdf format is available on some websites,
166 * search for "CIFFspecV1R03.pdf" or "CIFFspecV1R04.pdf"
168 if (len < CRW_HEADER_SIZE ||
169 memcmp(data, "II", 2) != 0 ||
170 memcmp(data + 6, "HEAPCCDR", 8) != 0)
175 block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER);
177 /* the end of the root block equals end of file,
178 * the last 4 bytes of the root block contain the block's data size
181 data_length = exif_byte_get_int32(data + offset, CRW_BYTE_ORDER);
183 offset = block_offset + data_length;
184 if (len < offset + 2) return FALSE;
186 /* number of directory entries for this block is in
187 * the next two bytes after the data for this block.
189 count = exif_byte_get_int16(data + offset, CRW_BYTE_ORDER);
191 if (len < offset + count * CRW_DIR_ENTRY_SIZE + 4) return FALSE;
193 /* walk the directory entries looking for type jpeg (tag 0x2007),
194 * for reference, other tags are 0x2005 for raw and 0x300a for photo info:
196 for (i = 0; i < count ; i++)
203 entry_offset = offset + i * CRW_DIR_ENTRY_SIZE;
205 /* entry is 10 bytes (in order):
207 * 4 for length of data
208 * 4 for offset into data segment of this block
210 record_type = exif_byte_get_int16(data + entry_offset, CRW_BYTE_ORDER);
211 record_length = exif_byte_get_int32(data + entry_offset + 2, CRW_BYTE_ORDER);
212 record_offset = exif_byte_get_int32(data + entry_offset + 6, CRW_BYTE_ORDER);
214 /* tag we want for jpeg data */
215 if (record_type == 0x2007)
219 jpeg_offset = block_offset + record_offset;
220 if (len < jpeg_offset + record_length ||
222 memcmp(data + jpeg_offset, "\xff\xd8\xff\xdb", 4) != 0)
227 /* we now know offset and verified jpeg */
228 *image_offset = jpeg_offset;
238 *-----------------------------------------------------------------------------
239 * EXIF Makernote for Canon
240 *-----------------------------------------------------------------------------
243 static ExifTextList CanonSet1MacroMode[] = {
249 static ExifTextList CanonSet1Quality[] = {
257 static ExifTextList CanonSet1FlashMode[] = {
258 { 0, "flash not fired" },
261 { 3, "red-eye reduction" },
263 { 5, "auto + red-eye reduction" },
264 { 6, "on + red-eye reduction" },
265 { 16, "external flash" },
269 static ExifTextList CanonSet1DriveMode[] = {
270 { 0, "single or timer" },
275 static ExifTextList CanonSet1FocusMode[] = {
276 { 0, "one-shot AF" },
277 { 1, "AI servo AF" },
278 { 2, "AI focus AF" },
286 static ExifTextList CanonSet1ImageSize[] = {
290 /* where (or) does Medium 1/2 fit in here ? */
294 static ExifTextList CanonSet1ShootingMode[] = {
298 { 3, "fast shutter" },
299 { 4, "slow shutter" },
301 { 6, "black and white" },
310 /* Don't think this is interpreted correctly/completely, A60 at 2.5x Digital sets value of 3 */
311 static ExifTextList CanonSet1DigitalZoom[] = {
319 static ExifTextList CanonSet1ConSatSharp[] = {
326 static ExifTextList CanonSet1ISOSpeed[] = {
327 /* { 0, "not set/see EXIF tag" }, */
336 static ExifTextList CanonSet1MeteringMode[] = {
341 { 5, "center-weighted" },
345 static ExifTextList CanonSet1FocusType[] = {
355 static ExifTextList CanonSet1AutoFocusPoint[] = {
356 { 0x2005, "manual AF point selection" },
357 { 0x3000, "manual focus" },
360 { 0x3003, "center" },
362 { 0x4001, "auto AF point selection" },
366 static ExifTextList CanonSet1ExposureMode[] = {
369 { 2, "Tv priority" },
370 { 3, "Av priority" },
376 static ExifTextList CanonSet1FlashFired[] = {
382 static ExifTextList CanonSet1FocusCont[] = {
383 { 0, "no (single)" },
388 static ExifMarker CanonSet1[] = {
389 /* 0 is length of array in bytes (2 x array size) */
390 { 1, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MacroMode", "Macro mode", CanonSet1MacroMode },
391 { 2, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SelfTimer", "Self timer (10ths of second)", NULL },
392 { 3, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Quality", "Quality", CanonSet1Quality },
393 { 4, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashMode", "Flash mode", CanonSet1FlashMode },
394 { 5, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DriveMode", "Drive mode", CanonSet1DriveMode },
395 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusMode", "Focus mode", CanonSet1FocusMode },
396 { 10, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ImageSize", "Image size", CanonSet1ImageSize },
397 { 11, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShootingMode","Shooting mode", CanonSet1ShootingMode },
398 { 11, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram", "ExposureProgram", CanonSet1ShootingMode },
399 { 12, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DigitalZoom", "Digital zoom", CanonSet1DigitalZoom },
400 { 13, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Contrast", "Contrast", CanonSet1ConSatSharp },
401 { 14, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Saturation", "Saturation", CanonSet1ConSatSharp },
402 { 15, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Sharpness", "Sharpness", CanonSet1ConSatSharp },
403 { 16, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ISOSpeed", "ISO speed", CanonSet1ISOSpeed },
404 { 16, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ISOSpeedRatings", "ISO speed", CanonSet1ISOSpeed },
405 { 17, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MeteringMode","Metering mode", CanonSet1MeteringMode },
406 { 18, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusType", "Focus type", CanonSet1FocusType },
407 { 19, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoFocus", "AutoFocus point", CanonSet1AutoFocusPoint },
408 { 20, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ExposureMode","Exposure mode", CanonSet1ExposureMode },
409 { 20, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode", "Exposure mode", CanonSet1ExposureMode },
410 { 23, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthLong","Long focal length", NULL },
411 { 24, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthShort","Short focal length", NULL },
412 { 25, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthUnits","Focal units per mm", NULL },
413 { 28, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashFired", "Flash fired", CanonSet1FlashFired },
414 { 29, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashDetails","Flash details", NULL },
415 { 32, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ContinuousFocus","Continuous focus", CanonSet1FocusCont },
419 static ExifTextList CanonSet2WhiteBalance[] = {
424 { 4, "fluorescent" },
427 { 7, "black and white" },
430 { 14, "daylight fluorescent" },
431 { 17, "underwater" },
435 static ExifTextList CanonSet2FlashBias[] = {
456 static ExifMarker CanonSet2[] = {
457 /* 0 is length of array in bytes (2 x array size) */
458 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.WhiteBalance","White balance", CanonSet2WhiteBalance },
459 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource", "White balance", CanonSet2WhiteBalance },
460 { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SequenceNumber","Sequence number", NULL },
461 { 15, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashBias", "Flash bias", CanonSet2FlashBias },
462 /* distance needs more than just this (metric) value */
463 { 19, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SubjectDistance", "Subject Distance", NULL },
469 static ExifTextList CanonCustomEnable[] = {
475 static ExifTextList CanonCustomEnableInvert[] = {
481 static ExifTextList CanonCustomExposureLevel[] = {
487 static ExifTextList CanonCustomAVShutterSpeed[] = {
489 { 1, "1/200 (fixed)" },
493 static ExifTextList CanonCustomShutterCurtainSync[] = {
499 static ExifMarker CanonCustom[] = {
500 { 1, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.NoiseReduction", "Noise reduction", CanonCustomEnable },
501 /*{ 2, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.BtnFuncShutter",
502 "Shutter/Auto exposure button function",CanonCustomBTNShutter }, */
503 { 3, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MirrorLockup", "Mirror lockup", CanonCustomEnable },
504 { 4, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.TvAvExposureLevel",
505 "Tv/Av and exposure level", CanonCustomExposureLevel },
506 { 5, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AFAssistLight", "AF assist light", CanonCustomEnableInvert },
507 { 6, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AvShutterSpeed",
508 "Shutter speed in Av mode", CanonCustomAVShutterSpeed },
509 /*{ 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoBracket",
510 "Auto-Exposure bracketting sequence/auto cancellation", CanonCustom }, */
511 { 8, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShutterSync", "Shutter sync", CanonCustomShutterCurtainSync },
512 /* { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.BtnFuncAF", "AF button function", CanonCustom }, */
513 { 10, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FillFlashReduction",
514 "Fill flash auto reduction", CanonCustomEnableInvert },
515 /*{ 11, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.BtnFuncMenu",
516 "Menu button function", CanonCustom }, */
517 /*{ 12, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.BtnFuncSet", "Set button function", CanonCustom }, */
518 { 13, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SensorCleaning", "Sensor cleaning", CanonCustomEnable },
524 static ExifMarker CanonExifMarkersList[] = {
525 { 1, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings1", NULL, NULL },
526 { 4, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings2", NULL, NULL },
527 { 6, EXIF_FORMAT_STRING, -1, "MkN.Canon.ImageType", "Image type", NULL },
528 { 7, EXIF_FORMAT_STRING, -1, "MkN.Canon.FirmwareVersion", "Firmware version", NULL },
529 { 8, EXIF_FORMAT_LONG_UNSIGNED, 1, "MkN.Canon.ImageNumber", "Image number", NULL },
530 { 9, EXIF_FORMAT_STRING, -1, "MkN.Canon.OwnerName", "Owner name", NULL },
531 { 12, EXIF_FORMAT_LONG_UNSIGNED, -1, "MkN.Canon.SerialNumber", "Camera serial number", NULL },
532 { 15, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.CustomFunctions", NULL, NULL },
536 static void canon_mknote_parse_settings(ExifData *exif,
537 guint16 *data, guint32 len, ExifByteOrder bo,
543 while (list[i].tag != 0)
545 if (list[i].tag < len)
549 item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
550 exif_item_copy_data(item, &data[list[i].tag], 2, EXIF_FORMAT_SHORT_UNSIGNED, bo);
551 exif->items = g_list_prepend(exif->items, item);
559 static void canon_mknote_parse_convert(ExifData *exif)
564 /* seems we need more than only this value for distance */
565 if (exif_get_integer(exif, "MkN.Canon.SubjectDistance", &value))
567 static ExifMarker marker= { 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,
568 "SubjectDistance", "Subject distance", NULL };
570 ExifRational *rational;
572 item = exif_item_new(marker.format, marker.tag, 1, &marker);
573 rational = item->data;
574 rational->num = value;
577 exif->items = g_list_prepend(exif->items, item);
580 result = exif_get_item(exif, "MkN.Canon.SerialNumber");
581 if (result && result->format == EXIF_FORMAT_LONG_UNSIGNED && result->data_len == 4)
583 static ExifMarker marker= { 12, EXIF_FORMAT_STRING, -1,
584 "SerialNumber", "Camera serial number", NULL };
590 n = (guint32)((guint32 *)(result->data))[0];
591 text = g_strdup_printf("%04X%05d", n & 0xffff0000 >> 8, n & 0x0000ffff);
592 l = strlen(text) + 1;
593 item = exif_item_new(marker.format, marker.tag, l, &marker);
594 memcpy(item->data, text, l);
597 exif->items = g_list_prepend(exif->items, item);
602 gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
603 guint size, ExifByteOrder bo)
607 if (exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, CanonExifMarkersList) != 0)
612 item = exif_get_item(exif, "MkN.Canon.Settings1");
615 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet1);
618 item = exif_get_item(exif, "MkN.Canon.Settings2");
621 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet2);
625 canon_mknote_parse_convert(exif);