2 * Copyright (C) 2005 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
5 * Author: Daniel M. German <dmgerman@uvic.ca>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 #include "format-canon.h"
36 #include "format-raw.h"
42 *-----------------------------------------------------------------------------
43 * Raw (CR2, CRW) embedded jpeg extraction for Canon
44 *-----------------------------------------------------------------------------
47 static gboolean canon_cr2_tiff_entry(guchar *data, const guint len, guint offset, ExifByteOrder bo,
48 guint *image_offset, gint *jpeg_encoding)
55 /* the two (tiff compliant) tags we want are:
56 * 0x0103 image compression type (must be type 6 for jpeg)
57 * 0x0111 jpeg start offset
58 * only use the first segment that contains an actual jpeg - as there
59 * is a another that contains the raw data.
61 tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo);
62 type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
63 count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo);
65 /* tag 0x0103 contains the compression type for this segment's image data */
68 if (ExifFormatList[type].size * count == 2 &&
69 exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_DATA, bo) == 6)
71 *jpeg_encoding = TRUE;
76 /* find and verify jpeg offset */
78 !jpeg_encoding) return FALSE;
80 /* make sure data segment contains 4 bytes */
81 if (ExifFormatList[type].size * count != 4) return FALSE;
83 jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
85 /* verify this is jpeg data */
86 if (len < jpeg_start + 4 ||
87 memcmp(data + jpeg_start, "\xff\xd8", 2) != 0)
92 *image_offset = jpeg_start;
96 static gint canon_cr2_tiff_table(guchar *data, const guint len, guint offset, ExifByteOrder bo,
99 gboolean jpeg_encoding = FALSE;
103 if (len < offset + 2) return 0;
105 count = exif_byte_get_int16(data + offset, bo);
107 if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0;
109 for (i = 0; i < count; i++)
111 if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo,
112 image_offset, &jpeg_encoding))
118 return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
121 gboolean format_canon_raw_cr2(guchar *data, const guint len,
122 guint *image_offset, guint *)
124 guint jpeg_offset = 0;
129 /* cr2 files are tiff files with a few canon specific directory tags
130 * they are (always ?) in little endian format
132 if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
135 while (offset && level < EXIF_TIFF_MAX_LEVELS)
137 offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset);
140 if (jpeg_offset != 0)
142 if (image_offset) *image_offset = jpeg_offset;
150 #define CRW_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
151 #define CRW_HEADER_SIZE 26
152 #define CRW_DIR_ENTRY_SIZE 10
154 gboolean format_canon_raw_crw(guchar *data, const guint len,
155 guint *image_offset, guint *)
163 /* CRW header starts with 2 bytes for byte order (always "II", little endian),
164 * 4 bytes for start of root block,
165 * and 8 bytes of magic for file type and format "HEAPCCDR"
166 * (also 4 bytes for file version, and 8 bytes reserved)
168 * CIFF specification in pdf format is available on some websites,
169 * search for "CIFFspecV1R03.pdf" or "CIFFspecV1R04.pdf"
171 if (len < CRW_HEADER_SIZE ||
172 memcmp(data, "II", 2) != 0 ||
173 memcmp(data + 6, "HEAPCCDR", 8) != 0)
178 block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER);
180 /* the end of the root block equals end of file,
181 * the last 4 bytes of the root block contain the block's data size
184 data_length = exif_byte_get_int32(data + offset, CRW_BYTE_ORDER);
186 offset = block_offset + data_length;
187 if (len < offset + 2) return FALSE;
189 /* number of directory entries for this block is in
190 * the next two bytes after the data for this block.
192 count = exif_byte_get_int16(data + offset, CRW_BYTE_ORDER);
194 if (len < offset + count * CRW_DIR_ENTRY_SIZE + 4) return FALSE;
196 /* walk the directory entries looking for type jpeg (tag 0x2007),
197 * for reference, other tags are 0x2005 for raw and 0x300a for photo info:
199 for (i = 0; i < count ; i++)
206 entry_offset = offset + i * CRW_DIR_ENTRY_SIZE;
208 /* entry is 10 bytes (in order):
210 * 4 for length of data
211 * 4 for offset into data segment of this block
213 record_type = exif_byte_get_int16(data + entry_offset, CRW_BYTE_ORDER);
214 record_length = exif_byte_get_int32(data + entry_offset + 2, CRW_BYTE_ORDER);
215 record_offset = exif_byte_get_int32(data + entry_offset + 6, CRW_BYTE_ORDER);
217 /* tag we want for jpeg data */
218 if (record_type == 0x2007)
222 jpeg_offset = block_offset + record_offset;
223 if (len < jpeg_offset + record_length ||
225 memcmp(data + jpeg_offset, "\xff\xd8\xff\xdb", 4) != 0)
230 /* we now know offset and verified jpeg */
231 *image_offset = jpeg_offset;
241 *-----------------------------------------------------------------------------
242 * EXIF Makernote for Canon
243 *-----------------------------------------------------------------------------
246 static ExifTextList CanonSet1MacroMode[] = {
252 static ExifTextList CanonSet1Quality[] = {
260 static ExifTextList CanonSet1FlashMode[] = {
261 { 0, "flash not fired" },
264 { 3, "red-eye reduction" },
266 { 5, "auto + red-eye reduction" },
267 { 6, "on + red-eye reduction" },
268 { 16, "external flash" },
272 static ExifTextList CanonSet1DriveMode[] = {
273 { 0, "single or timer" },
278 static ExifTextList CanonSet1FocusMode[] = {
279 { 0, "one-shot AF" },
280 { 1, "AI servo AF" },
281 { 2, "AI focus AF" },
289 static ExifTextList CanonSet1ImageSize[] = {
293 /* where (or) does Medium 1/2 fit in here ? */
297 static ExifTextList CanonSet1ShootingMode[] = {
301 { 3, "fast shutter" },
302 { 4, "slow shutter" },
304 { 6, "black and white" },
313 /* Don't think this is interpreted correctly/completely, A60 at 2.5x Digital sets value of 3 */
314 static ExifTextList CanonSet1DigitalZoom[] = {
322 static ExifTextList CanonSet1ConSatSharp[] = {
329 static ExifTextList CanonSet1ISOSpeed[] = {
330 /* { 0, "not set/see EXIF tag" }, */
339 static ExifTextList CanonSet1MeteringMode[] = {
344 { 5, "center-weighted" },
348 static ExifTextList CanonSet1FocusType[] = {
358 static ExifTextList CanonSet1AutoFocusPoint[] = {
359 { 0x2005, "manual AF point selection" },
360 { 0x3000, "manual focus" },
363 { 0x3003, "center" },
365 { 0x4001, "auto AF point selection" },
369 static ExifTextList CanonSet1ExposureMode[] = {
372 { 2, "Tv priority" },
373 { 3, "Av priority" },
379 static ExifTextList CanonSet1FlashFired[] = {
385 static ExifTextList CanonSet1FocusCont[] = {
386 { 0, "no (single)" },
391 static ExifMarker CanonSet1[] = {
392 /* 0 is length of array in bytes (2 x array size) */
393 { 1, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MacroMode", "Macro mode", CanonSet1MacroMode },
394 { 2, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SelfTimer", "Self timer (10ths of second)", nullptr },
395 { 3, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Quality", "Quality", CanonSet1Quality },
396 { 4, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashMode", "Flash mode", CanonSet1FlashMode },
397 { 5, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DriveMode", "Drive mode", CanonSet1DriveMode },
398 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusMode", "Focus mode", CanonSet1FocusMode },
399 { 10, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ImageSize", "Image size", CanonSet1ImageSize },
400 { 11, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShootingMode","Shooting mode", CanonSet1ShootingMode },
401 { 11, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram", "ExposureProgram", CanonSet1ShootingMode },
402 { 12, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DigitalZoom", "Digital zoom", CanonSet1DigitalZoom },
403 { 13, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Contrast", "Contrast", CanonSet1ConSatSharp },
404 { 14, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Saturation", "Saturation", CanonSet1ConSatSharp },
405 { 15, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Sharpness", "Sharpness", CanonSet1ConSatSharp },
406 { 16, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ISOSpeed", "ISO speed", CanonSet1ISOSpeed },
407 { 16, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ISOSpeedRatings", "ISO speed", CanonSet1ISOSpeed },
408 { 17, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MeteringMode","Metering mode", CanonSet1MeteringMode },
409 { 18, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusType", "Focus type", CanonSet1FocusType },
410 { 19, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoFocus", "AutoFocus point", CanonSet1AutoFocusPoint },
411 { 20, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ExposureMode","Exposure mode", CanonSet1ExposureMode },
412 { 20, EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode", "Exposure mode", CanonSet1ExposureMode },
413 { 23, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthLong","Long focal length", nullptr },
414 { 24, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthShort","Short focal length", nullptr },
415 { 25, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthUnits","Focal units per mm", nullptr },
416 { 28, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashFired", "Flash fired", CanonSet1FlashFired },
417 { 29, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashDetails","Flash details", nullptr },
418 { 32, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ContinuousFocus","Continuous focus", CanonSet1FocusCont },
422 static ExifTextList CanonSet2WhiteBalance[] = {
427 { 4, "fluorescent" },
430 { 7, "black and white" },
433 { 14, "daylight fluorescent" },
434 { 17, "underwater" },
438 static ExifTextList CanonSet2FlashBias[] = {
459 static ExifMarker CanonSet2[] = {
460 /* 0 is length of array in bytes (2 x array size) */
461 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.WhiteBalance","White balance", CanonSet2WhiteBalance },
462 { 7, EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource", "White balance", CanonSet2WhiteBalance },
463 { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SequenceNumber","Sequence number", nullptr },
464 { 15, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashBias", "Flash bias", CanonSet2FlashBias },
465 /* distance needs more than just this (metric) value */
466 { 19, EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SubjectDistance", "Subject Distance", nullptr },
471 static ExifMarker CanonExifMarkersList[] = {
472 { 1, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings1", nullptr, nullptr },
473 { 4, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings2", nullptr, nullptr },
474 { 6, EXIF_FORMAT_STRING, -1, "MkN.Canon.ImageType", "Image type", nullptr },
475 { 7, EXIF_FORMAT_STRING, -1, "MkN.Canon.FirmwareVersion", "Firmware version", nullptr },
476 { 8, EXIF_FORMAT_LONG_UNSIGNED, 1, "MkN.Canon.ImageNumber", "Image number", nullptr },
477 { 9, EXIF_FORMAT_STRING, -1, "MkN.Canon.OwnerName", "Owner name", nullptr },
478 { 12, EXIF_FORMAT_LONG_UNSIGNED, -1, "MkN.Canon.SerialNumber", "Camera serial number", nullptr },
479 { 15, EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.CustomFunctions", nullptr, nullptr },
483 static void canon_mknote_parse_settings(ExifData *exif,
484 guint16 *data, guint32 len, ExifByteOrder bo,
490 while (list[i].tag != 0)
492 if (list[i].tag < len)
496 item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
497 exif_item_copy_data(item, &data[list[i].tag], 2, EXIF_FORMAT_SHORT_UNSIGNED, bo);
498 exif->items = g_list_prepend(exif->items, item);
506 gboolean format_canon_makernote(ExifData *exif, guchar *tiff, guint offset,
507 guint size, ExifByteOrder bo)
511 if (exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, CanonExifMarkersList) != 0)
516 item = exif_get_item(exif, "MkN.Canon.Settings1");
519 canon_mknote_parse_settings(exif, static_cast<guint16*>(item->data), item->data_len, bo, CanonSet1);
522 item = exif_get_item(exif, "MkN.Canon.Settings2");
525 canon_mknote_parse_settings(exif, static_cast<guint16*>(item->data), item->data_len, bo, CanonSet2);
532 using dummy_variable = int;
535 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */