c50b40dc207e611db175a2b65e083ed6d9d87194
[geeqie.git] / src / format_canon.c
1 /*
2  * Geeqie
3  * (C) 2005 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * This software is released under the GNU General Public License (GNU GPL).
7  * Please read the included file COPYING for more information.
8  * This software comes with no warranty of any kind, use at your own risk!
9  *
10  *
11  * Code to add support for Canon CR2 and CRW files, version 0.2
12  *
13  * Developed by Daniel M. German, dmgerman at uvic.ca
14  *
15  * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/
16  *
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #endif
22
23 #ifndef HAVE_EXIV2
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <glib.h>
30
31 #include "intl.h"
32
33 #include "main.h"
34 #include "format_canon.h"
35 #include "format_raw.h"
36
37 #include "exif.h"
38
39
40 /*
41  *-----------------------------------------------------------------------------
42  * Raw (CR2, CRW) embedded jpeg extraction for Canon
43  *-----------------------------------------------------------------------------
44  */
45
46 static gboolean canon_cr2_tiff_entry(guchar *data, const guint len, guint offset, ExifByteOrder bo,
47                                      guint *image_offset, gint *jpeg_encoding)
48 {
49         guint tag;
50         guint type;
51         guint count;
52         guint jpeg_start;
53
54         /* the two (tiff compliant) tags we want are:
55          *  0x0103 image compression type (must be type 6 for jpeg)
56          *  0x0111 jpeg start offset
57          * only use the first segment that contains an actual jpeg - as there
58          * is a another that contains the raw data.
59          */
60         tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo);
61         type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
62         count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo);
63
64         /* tag 0x0103 contains the compression type for this segment's image data */
65         if (tag == 0x0103)
66                 {
67                 if (ExifFormatList[type].size * count == 2 &&
68                     exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_DATA, bo) == 6)
69                         {
70                         *jpeg_encoding = TRUE;
71                         }
72                 return FALSE;
73                 }
74
75         /* find and verify jpeg offset */
76         if (tag != 0x0111 ||
77             !jpeg_encoding) return FALSE;
78
79         /* make sure data segment contains 4 bytes */
80         if (ExifFormatList[type].size * count != 4) return FALSE;
81
82         jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
83
84         /* verify this is jpeg data */
85         if (len < jpeg_start + 4 ||
86             memcmp(data + jpeg_start, "\xff\xd8", 2) != 0)
87                 {
88                 return FALSE;
89                 }
90
91         *image_offset = jpeg_start;
92         return TRUE;
93 }
94
95 static gint canon_cr2_tiff_table(guchar *data, const guint len, guint offset, ExifByteOrder bo,
96                                  guint *image_offset)
97 {
98         gboolean jpeg_encoding = FALSE;
99         guint count;
100         guint i;
101
102         if (len < offset + 2) return 0;
103
104         count = exif_byte_get_int16(data + offset, bo);
105         offset += 2;
106         if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0;
107
108         for (i = 0; i < count; i++)
109                 {
110                 if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo,
111                                          image_offset, &jpeg_encoding))
112                         {
113                         return 0;
114                         }
115                 }
116
117         return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
118 }
119
120 gboolean format_canon_raw_cr2(guchar *data, const guint len,
121                               guint *image_offset, guint *exif_offset)
122 {
123         guint jpeg_offset = 0;
124         ExifByteOrder bo;
125         guint offset;
126         gint level;
127
128         /* cr2 files are tiff files with a few canon specific directory tags
129          * they are (always ?) in little endian format
130          */
131         if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
132
133         level = 0;
134         while (offset && level < EXIF_TIFF_MAX_LEVELS)
135                 {
136                 offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset);
137                 level++;
138
139                 if (jpeg_offset != 0)
140                         {
141                         if (image_offset) *image_offset = jpeg_offset;
142                         return TRUE;
143                         }
144                 }
145
146         return FALSE;
147 }
148
149 #define CRW_BYTE_ORDER          EXIF_BYTE_ORDER_INTEL
150 #define CRW_HEADER_SIZE         26
151 #define CRW_DIR_ENTRY_SIZE      10
152
153 gboolean format_canon_raw_crw(guchar *data, const guint len,
154                               guint *image_offset, guint *exif_offset)
155 {
156         guint block_offset;
157         guint data_length;
158         guint offset;
159         guint count;
160         guint i;
161
162         /* CRW header starts with 2 bytes for byte order (always "II", little endian),
163          * 4 bytes for start of root block,
164          * and 8 bytes of magic for file type and format "HEAPCCDR"
165          * (also 4 bytes for file version, and 8 bytes reserved)
166          *
167          * CIFF specification in pdf format is available on some websites,
168          * search for "CIFFspecV1R03.pdf" or "CIFFspecV1R04.pdf"
169          */
170         if (len < CRW_HEADER_SIZE ||
171             memcmp(data, "II", 2) != 0 ||
172             memcmp(data + 6, "HEAPCCDR", 8) != 0)
173                 {
174                 return FALSE;
175                 }
176
177         block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER);
178
179         /* the end of the root block equals end of file,
180          * the last 4 bytes of the root block contain the block's data size
181          */
182         offset = len - 4;
183         data_length = exif_byte_get_int32(data + offset, CRW_BYTE_ORDER);
184
185         offset = block_offset + data_length;
186         if (len < offset + 2) return FALSE;
187
188         /* number of directory entries for this block is in
189          * the next two bytes after the data for this block.
190          */
191         count = exif_byte_get_int16(data + offset, CRW_BYTE_ORDER);
192         offset += 2;
193         if (len < offset + count * CRW_DIR_ENTRY_SIZE + 4) return FALSE;
194
195         /* walk the directory entries looking for type jpeg (tag 0x2007),
196          * for reference, other tags are 0x2005 for raw and 0x300a for photo info:
197          */
198         for (i = 0; i < count ; i++)
199                 {
200                 guint entry_offset;
201                 guint record_type;
202                 guint record_offset;
203                 guint record_length;
204
205                 entry_offset = offset + i * CRW_DIR_ENTRY_SIZE;
206
207                 /* entry is 10 bytes (in order):
208                  *  2 for type
209                  *  4 for length of data
210                  *  4 for offset into data segment of this block
211                  */
212                 record_type = exif_byte_get_int16(data + entry_offset, CRW_BYTE_ORDER);
213                 record_length = exif_byte_get_int32(data + entry_offset + 2, CRW_BYTE_ORDER);
214                 record_offset = exif_byte_get_int32(data + entry_offset + 6, CRW_BYTE_ORDER);
215
216                 /* tag we want for jpeg data */
217                 if (record_type == 0x2007)
218                         {
219                         guint jpeg_offset;
220
221                         jpeg_offset = block_offset + record_offset;
222                         if (len < jpeg_offset + record_length ||
223                             record_length < 4 ||
224                             memcmp(data + jpeg_offset, "\xff\xd8\xff\xdb", 4) != 0)
225                                 {
226                                 return FALSE;
227                                 }
228
229                         /* we now know offset and verified jpeg */
230                         *image_offset = jpeg_offset;
231                         return TRUE;
232                         }
233                 }
234
235         return FALSE;
236 }
237
238
239 /*
240  *-----------------------------------------------------------------------------
241  * EXIF Makernote for Canon
242  *-----------------------------------------------------------------------------
243  */
244
245 static ExifTextList CanonSet1MacroMode[] = {
246         { 1,    "macro" },
247         { 2,    "normal" },
248         EXIF_TEXT_LIST_END
249 };
250
251 static ExifTextList CanonSet1Quality[] = {
252         { 2,    "normal" },
253         { 3,    "fine" },
254         { 4,    "raw" },
255         { 5,    "superfine" },
256         EXIF_TEXT_LIST_END
257 };
258
259 static ExifTextList CanonSet1FlashMode[] = {
260         { 0,    "flash not fired" },
261         { 1,    "auto" },
262         { 2,    "on" },
263         { 3,    "red-eye reduction" },
264         { 4,    "slow sync" },
265         { 5,    "auto + red-eye reduction" },
266         { 6,    "on + red-eye reduction" },
267         { 16,   "external flash" },
268         EXIF_TEXT_LIST_END
269 };
270
271 static ExifTextList CanonSet1DriveMode[] = {
272         { 0,    "single or timer" },
273         { 1,    "continuous" },
274         EXIF_TEXT_LIST_END
275 };
276
277 static ExifTextList CanonSet1FocusMode[] = {
278         { 0,    "one-shot AF" },
279         { 1,    "AI servo AF" },
280         { 2,    "AI focus AF" },
281         { 3,    "manual" },
282         { 4,    "single" },
283         { 5,    "continuous" },
284         { 6,    "manual" },
285         EXIF_TEXT_LIST_END
286 };
287
288 static ExifTextList CanonSet1ImageSize[] = {
289         { 0,    "large" },
290         { 1,    "medium" },
291         { 2,    "small" },
292         /* where (or) does Medium 1/2 fit in here ? */
293         EXIF_TEXT_LIST_END
294 };
295
296 static ExifTextList CanonSet1ShootingMode[] = {
297         { 0,    "auto" },
298         { 1,    "manual" },
299         { 2,    "landscape" },
300         { 3,    "fast shutter" },
301         { 4,    "slow shutter" },
302         { 5,    "night" },
303         { 6,    "black and white" },
304         { 7,    "sepia" },
305         { 8,    "portrait" },
306         { 9,    "sports" },
307         { 10,   "macro" },
308         { 11,   "pan focus" },
309         EXIF_TEXT_LIST_END
310 };
311
312 /* Don't think this is interpreted correctly/completely, A60 at 2.5x Digital sets value of 3 */
313 static ExifTextList CanonSet1DigitalZoom[] = {
314         { 0,    "none" },
315         { 1,    "2x" },
316         { 2,    "4x" },
317         { 3,    "other" },
318         EXIF_TEXT_LIST_END
319 };
320
321 static ExifTextList CanonSet1ConSatSharp[] = {
322         { 0,    "normal" },
323         { 1,    "high" },
324         { 65535,"low" },
325         EXIF_TEXT_LIST_END
326 };
327
328 static ExifTextList CanonSet1ISOSpeed[] = {
329 /*      { 0,    "not set/see EXIF tag" }, */
330         { 15,   "auto" },
331         { 16,   "50" },
332         { 17,   "100" },
333         { 18,   "200" },
334         { 19,   "400" },
335         EXIF_TEXT_LIST_END
336 };
337
338 static ExifTextList CanonSet1MeteringMode[] = {
339         { 0,    "default" },
340         { 1,    "spot" },
341         { 3,    "evaluative" },
342         { 4,    "partial" },
343         { 5,    "center-weighted" },
344         EXIF_TEXT_LIST_END
345 };
346
347 static ExifTextList CanonSet1FocusType[] = {
348         { 0,    "manual" },
349         { 1,    "auto" },
350         { 2,    "auto" },
351         { 3,    "macro" },
352         { 7,    "infinity" },
353         { 8,    "locked" },
354         EXIF_TEXT_LIST_END
355 };
356
357 static ExifTextList CanonSet1AutoFocusPoint[] = {
358         { 0x2005,       "manual AF point selection" },
359         { 0x3000,       "manual focus" },
360         { 0x3001,       "auto" },
361         { 0x3002,       "right" },
362         { 0x3003,       "center" },
363         { 0x3004,       "left" },
364         { 0x4001,       "auto AF point selection" },
365         EXIF_TEXT_LIST_END
366 };
367
368 static ExifTextList CanonSet1ExposureMode[] = {
369         { 0,    "auto" },
370         { 1,    "program" },
371         { 2,    "Tv priority" },
372         { 3,    "Av priority" },
373         { 4,    "manual" },
374         { 5,    "A-DEP" },
375         EXIF_TEXT_LIST_END
376 };
377
378 static ExifTextList CanonSet1FlashFired[] = {
379         { 0,    "no" },
380         { 1,    "yes" },
381         EXIF_TEXT_LIST_END
382 };
383
384 static ExifTextList CanonSet1FocusCont[] = {
385         { 0,    "no (single)" },
386         { 1,    "yes" },
387         EXIF_TEXT_LIST_END
388 };
389
390 static ExifMarker CanonSet1[] = {
391 /* 0 is length of array in bytes (2 x array size) */
392 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MacroMode",   "Macro mode",           CanonSet1MacroMode },
393 { 2,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SelfTimer",   "Self timer (10ths of second)", NULL },
394 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Quality",     "Quality",              CanonSet1Quality },
395 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashMode",   "Flash mode",           CanonSet1FlashMode },
396 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DriveMode",   "Drive mode",           CanonSet1DriveMode },
397 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusMode",   "Focus mode",           CanonSet1FocusMode },
398 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ImageSize",   "Image size",           CanonSet1ImageSize },
399 { 11,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShootingMode","Shooting mode",        CanonSet1ShootingMode },
400  { 11,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram",       "ExposureProgram",      CanonSet1ShootingMode },
401 { 12,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DigitalZoom", "Digital zoom",         CanonSet1DigitalZoom },
402 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Contrast",    "Contrast",             CanonSet1ConSatSharp },
403 { 14,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Saturation",  "Saturation",           CanonSet1ConSatSharp },
404 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Sharpness",   "Sharpness",            CanonSet1ConSatSharp },
405 { 16,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ISOSpeed",    "ISO speed",            CanonSet1ISOSpeed },
406  { 16,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ISOSpeedRatings",       "ISO speed",            CanonSet1ISOSpeed },
407 { 17,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MeteringMode","Metering mode",        CanonSet1MeteringMode },
408 { 18,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusType",   "Focus type",           CanonSet1FocusType },
409 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoFocus",   "AutoFocus point",      CanonSet1AutoFocusPoint },
410 { 20,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ExposureMode","Exposure mode",        CanonSet1ExposureMode },
411  { 20,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode",          "Exposure mode",        CanonSet1ExposureMode },
412 { 23,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthLong","Long focal length", NULL },
413 { 24,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthShort","Short focal length", NULL },
414 { 25,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthUnits","Focal units per mm", NULL },
415 { 28,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashFired",  "Flash fired",          CanonSet1FlashFired },
416 { 29,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashDetails","Flash details",        NULL },
417 { 32,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ContinuousFocus","Continuous focus",  CanonSet1FocusCont },
418 EXIF_MARKER_LIST_END
419 };
420
421 static ExifTextList CanonSet2WhiteBalance[] = {
422         { 0,    "auto" },
423         { 1,    "sunny" },
424         { 2,    "cloudy" },
425         { 3,    "tungsten" },
426         { 4,    "fluorescent" },
427         { 5,    "flash" },
428         { 6,    "custom" },
429         { 7,    "black and white" },
430         { 8,    "shade" },
431         { 9,    "manual" },
432         { 14,   "daylight fluorescent" },
433         { 17,   "underwater" },
434         EXIF_TEXT_LIST_END
435 };
436
437 static ExifTextList CanonSet2FlashBias[] = {
438         { 0x0000,       "0" },
439         { 0x000c,       "0.33" },
440         { 0x0010,       "0.5" },
441         { 0x0014,       "0.67" },
442         { 0x0020,       "1" },
443         { 0x002c,       "1.33" },
444         { 0x0030,       "1.5" },
445         { 0x0034,       "1.67" },
446         { 0x0040,       "2" },
447         { 0xffc0,       "-2" },
448         { 0xffcc,       "-1.67" },
449         { 0xffd0,       "-1.5" },
450         { 0xffd4,       "-1.33" },
451         { 0xffe0,       "-1" },
452         { 0xffec,       "-0.67" },
453         { 0xfff0,       "-0.5" },
454         { 0xfff4,       "-0.33" },
455         EXIF_TEXT_LIST_END
456 };
457
458 static ExifMarker CanonSet2[] = {
459 /* 0 is length of array in bytes (2 x array size) */
460 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.WhiteBalance","White balance",        CanonSet2WhiteBalance },
461  { 7,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource",           "White balance",        CanonSet2WhiteBalance },
462 { 9,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SequenceNumber","Sequence number",    NULL },
463 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashBias",   "Flash bias",           CanonSet2FlashBias },
464 /* distance needs more than just this (metric) value */
465 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SubjectDistance",     "Subject Distance", NULL },
466 EXIF_MARKER_LIST_END
467 };
468
469 #if 0
470
471 static ExifTextList CanonCustomEnable[] = {
472         { 0,    "off" },
473         { 1,    "on" },
474         EXIF_TEXT_LIST_END
475 };
476
477 static ExifTextList CanonCustomEnableInvert[] = {
478         { 0,    "on" },
479         { 1,    "off" },
480         EXIF_TEXT_LIST_END
481 };
482
483 static ExifTextList CanonCustomExposureLevel[] = {
484         { 0,    "1/2 stop" },
485         { 1,    "1/3 stop" },
486         EXIF_TEXT_LIST_END
487 };
488
489 static ExifTextList CanonCustomAVShutterSpeed[] = {
490         { 0,    "auto" },
491         { 1,    "1/200 (fixed)" },
492         EXIF_TEXT_LIST_END
493 };
494
495 static ExifTextList CanonCustomShutterCurtainSync[] = {
496         { 0,    "1st" },
497         { 1,    "2nd" },
498         EXIF_TEXT_LIST_END
499 };
500
501 static ExifMarker CanonCustom[] = {
502 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.NoiseReduction", "Noise reduction",  CanonCustomEnable },
503 /*{ 2,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncShutter",
504                                                 "Shutter/Auto exposure button function",CanonCustomBTNShutter }, */
505 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.MirrorLockup", "Mirror lockup",      CanonCustomEnable },
506 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.TvAvExposureLevel",
507                                                         "Tv/Av and exposure level",     CanonCustomExposureLevel },
508 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AFAssistLight", "AF assist light",   CanonCustomEnableInvert },
509 { 6,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AvShutterSpeed",
510                                                         "Shutter speed in Av mode",     CanonCustomAVShutterSpeed },
511 /*{ 7,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AutoBracket",
512                                 "Auto-Exposure bracketting sequence/auto cancellation", CanonCustom }, */
513 { 8,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.ShutterSync", "Shutter sync",        CanonCustomShutterCurtainSync },
514 /* { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncAF",  "AF button function",   CanonCustom }, */
515 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.FillFlashReduction",
516                                                         "Fill flash auto reduction",    CanonCustomEnableInvert },
517 /*{ 11, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncMenu",
518                                                         "Menu button function",         CanonCustom }, */
519 /*{ 12, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncSet", "Set button function",  CanonCustom }, */
520 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.SensorCleaning", "Sensor cleaning",  CanonCustomEnable },
521 EXIF_MARKER_LIST_END
522 };
523
524 #endif
525
526 static ExifMarker CanonExifMarkersList[] = {
527         { 1,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings1",          NULL, NULL },
528         { 4,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings2",          NULL, NULL },
529         { 6,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.ImageType",          "Image type", NULL },
530         { 7,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.FirmwareVersion",    "Firmware version", NULL },
531         { 8,    EXIF_FORMAT_LONG_UNSIGNED, 1,   "MkN.Canon.ImageNumber",        "Image number", NULL },
532         { 9,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.OwnerName",          "Owner name", NULL },
533         { 12,   EXIF_FORMAT_LONG_UNSIGNED, -1,  "MkN.Canon.SerialNumber",       "Camera serial number", NULL },
534         { 15,   EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.CustomFunctions",    NULL, NULL },
535         EXIF_MARKER_LIST_END
536 };
537
538 static void canon_mknote_parse_settings(ExifData *exif,
539                                         guint16 *data, guint32 len, ExifByteOrder bo,
540                                         ExifMarker *list)
541 {
542         gint i;
543
544         i = 0;
545         while (list[i].tag != 0)
546                 {
547                 if (list[i].tag < len)
548                         {
549                         ExifItem *item;
550
551                         item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
552                         exif_item_copy_data(item, &data[list[i].tag], 2, EXIF_FORMAT_SHORT_UNSIGNED, bo);
553                         exif->items = g_list_prepend(exif->items, item);
554                         }
555
556                 i++;
557                 }
558 }
559
560 #if 0
561 static void canon_mknote_parse_convert(ExifData *exif)
562 {
563         gint value;
564         ExifItem *result;
565
566         /* seems we need more than only this value for distance */
567         if (exif_get_integer(exif, "MkN.Canon.SubjectDistance", &value))
568                 {
569                 static ExifMarker marker= { 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,
570                                             "SubjectDistance", "Subject distance", NULL };
571                 ExifItem *item;
572                 ExifRational *rational;
573
574                 item = exif_item_new(marker.format, marker.tag, 1, &marker);
575                 rational = item->data;
576                 rational->num = value;
577                 rational->den = 100;
578
579                 exif->items = g_list_prepend(exif->items, item);
580                 }
581
582         result = exif_get_item(exif, "MkN.Canon.SerialNumber");
583         if (result && result->format == EXIF_FORMAT_LONG_UNSIGNED && result->data_len == 4)
584                 {
585                 static ExifMarker marker= { 12, EXIF_FORMAT_STRING, -1,
586                                             "SerialNumber", "Camera serial number", NULL };
587                 ExifItem *item;
588                 gchar *text;
589                 gint l;
590                 guint32 n;
591
592                 n = (guint32)((guint32 *)(result->data))[0];
593                 text = g_strdup_printf("%04X%05d", n & 0xffff0000 >> 8, n & 0x0000ffff);
594                 l = strlen(text) + 1;
595                 item = exif_item_new(marker.format, marker.tag, l, &marker);
596                 memcpy(item->data, text, l);
597                 g_free(text);
598
599                 exif->items = g_list_prepend(exif->items, item);
600                 }
601 }
602 #endif
603
604 gboolean format_canon_makernote(ExifData *exif, guchar *tiff, guint offset,
605                                 guint size, ExifByteOrder bo)
606 {
607         ExifItem *item;
608
609         if (exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, CanonExifMarkersList) != 0)
610                 {
611                 return FALSE;
612                 }
613
614         item = exif_get_item(exif, "MkN.Canon.Settings1");
615         if (item)
616                 {
617                 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet1);
618                 }
619
620         item = exif_get_item(exif, "MkN.Canon.Settings2");
621         if (item)
622                 {
623                 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet2);
624                 }
625
626 #if 0
627         canon_mknote_parse_convert(exif);
628 #endif
629
630         return TRUE;
631 }
632
633
634 #endif
635 /* not HAVE_EXIV2 */
636 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */