various exif improvements based on patch by Uwe Ohse
[geeqie.git] / src / exif-common.c
1 /*
2  *  GQView
3  *  (C) 2006 John Ellis
4  *
5 */
6
7 #ifdef HAVE_CONFIG_H
8 #  include "config.h"
9 #endif
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/mman.h>
18 #include <math.h>
19  
20 #include <glib.h>
21
22 #include "intl.h"
23
24 #include "gqview.h"
25 #include "exif.h"
26
27 #include "format_raw.h"
28 #include "ui_fileops.h"
29
30
31 /* human readable key list */
32
33 ExifFormattedText ExifFormattedList[] = {
34         { "fCamera",            N_("Camera") },
35         { "fDateTime",          N_("Date") },
36         { "fShutterSpeed",      N_("Shutter speed") },
37         { "fAperture",          N_("Aperture") },
38         { "fExposureBias",      N_("Exposure bias") },
39         { "fISOSpeedRating",    N_("ISO sensitivity") },
40         { "fFocalLength",       N_("Focal length") },
41         { "fFocalLength35mmFilm",N_("Focal length 35mm") },
42         { "fSubjectDistance",   N_("Subject distance") },
43         { "fFlash",             N_("Flash") },
44         { "fResolution",        N_("Resolution") },
45         { NULL, NULL }
46 };
47
48 static ExifTextList ExifFlashList[] = {
49         { 0,    N_("no") },
50         { 1,    N_("yes") },
51         { 5,    N_("yes, not detected by strobe") },
52         { 7,    N_("yes, detected by strobe") },
53         EXIF_TEXT_LIST_END
54 };
55
56 double exif_rational_to_double(ExifRational *r, gint sign)
57 {
58         if (!r || r->den == 0.0) return 0.0;
59
60         if (sign) return (double)((int)r->num) / (double)((int)r->den);
61         return (double)r->num / r->den;
62 }
63
64 double exif_get_rational_as_double(ExifData *exif, const gchar *key)
65 {
66         ExifRational *r;
67         gint sign;
68
69         r = exif_get_rational(exif, key, &sign);
70         return exif_rational_to_double(r, sign);
71 }
72
73 static GString *append_comma_text(GString *string, const gchar *text)
74 {
75         string = g_string_append(string, ", ");
76         string = g_string_append(string, text);
77
78         return string;
79 }
80
81 static gchar *remove_common_prefix(gchar *s, gchar *t)
82 {
83         gint i;
84
85         if (!s || !t) return t;
86
87         for (i = 0; s[i] == t[i]; i++)
88                 ;
89         if (!i) 
90                 return t;
91         if (s[i]==' ' || s[i]==0)
92                 {
93                 while (t[i] == ' ')
94                         i++;
95                 return t + i;
96                 }
97         return s;
98 }
99
100 static double get_crop_factor(ExifData *exif)
101 {
102         double res_unit_tbl[] = {0.0, 25.4, 25.4, 10.0, 1.0, 0.001 };
103
104         double xres = exif_get_rational_as_double(exif, "Exif.Photo.FocalPlaneXResolution");
105         double yres = exif_get_rational_as_double(exif, "Exif.Photo.FocalPlaneYResolution");
106         int res_unit;
107         int w, h;
108         double xsize, ysize, size, ratio;
109         
110         if (xres == 0.0 || yres == 0.0) return 0.0;
111         
112         if (!exif_get_integer(exif, "Exif.Photo.FocalPlaneResolutionUnit", &res_unit)) return 0.0;
113         if (res_unit < 1 || res_unit > 5) return 0.0;
114         
115         if (!exif_get_integer(exif, "Exif.Photo.PixelXDimension", &w)) return 0.0;
116         if (!exif_get_integer(exif, "Exif.Photo.PixelYDimension", &h)) return 0.0;
117         
118         xsize = w * res_unit_tbl[res_unit] / xres;
119         ysize = h * res_unit_tbl[res_unit] / yres;
120         
121         ratio = xsize / ysize;
122         
123         if (ratio < 0.5 || ratio > 2.0) return 0.0; /* reasonable ratio */
124         
125         size = sqrt(xsize * xsize + ysize * ysize);
126         
127         if (size < 1.0 || size > 100.0) return 0.0; /* reasonable sensor size in mm */
128         
129         return sqrt(36*36+24*24) / size;
130         
131 }
132
133
134 gchar *exif_get_formatted_by_key(ExifData *exif, const gchar *key, gint *key_valid)
135 {
136         /* must begin with f, else not formatted */
137         if (key[0] != 'f')
138                 {
139                 if (key_valid) *key_valid = FALSE;
140                 return NULL;
141                 }
142
143         if (key_valid) *key_valid = TRUE;
144
145         if (strcmp(key, "fCamera") == 0)
146                 {
147                 gchar *text;
148                 gchar *make = exif_get_data_as_text(exif, "Exif.Image.Make");
149                 gchar *model = exif_get_data_as_text(exif, "Exif.Image.Model");
150                 gchar *software = exif_get_data_as_text(exif, "Exif.Image.Software");
151                 gchar *model2;
152                 gchar *software2;
153                 gint i;
154
155                 if (make)
156                         {
157                         gchar *x;
158                         
159                         g_strstrip(make);
160 #define REMOVE_SUFFIX(str,suff)         \
161 do {                                    \
162         if (g_str_has_suffix(str,suff)) \
163                 str[strlen(str)-(sizeof(suff)-1)] = 0;  \
164 } while(0)
165                         REMOVE_SUFFIX(make," Corporation"); /* Pentax */
166                         REMOVE_SUFFIX(make," OPTICAL CO.,LTD"); /* OLYMPUS */
167                 }
168                 if (model)
169                         g_strstrip(model);
170                 if (software)
171                         g_strstrip(software);
172                 /* remove superfluous spaces (pentax K100D) */
173                 for (i=0; software && software[i]; i++)
174                         if (software[i] == ' ' && software[i+1] == ' ')
175                                 {
176                                 gint j;
177                                 
178                                 for (j=1; software[i+j]; j++)
179                                         if (software[i+j] != ' ')
180                                                 break;
181                                 memmove(software+i+1, software+i+j, strlen(software+i+j)+1);
182                                 }
183
184                 model2 = remove_common_prefix(make, model);
185                 software2 = remove_common_prefix(model2, software);
186
187                 text = g_strdup_printf("%s%s%s%s%s%s", (make) ? make : "", ((make) && (model)) ? " " : "",
188                                                        (model2) ? model2 : "",
189                                                        (software2) ? " (" : "",
190                                                        (software2) ? software2 : "",
191                                                        (software2) ? ")" : "");
192
193                 g_free(make);
194                 g_free(model);
195                 g_free(software);
196                 return text;
197                 }
198         if (strcmp(key, "fDateTime") == 0)
199                 {
200                 gchar *text = exif_get_data_as_text(exif, "Exif.Photo.DateTimeOriginal");
201                 gchar *subsec = NULL;
202                 if (text) subsec = exif_get_data_as_text(exif, "Exif.Photo.SubSecTimeOriginal");
203                 if (!text)
204                         {
205                         text = exif_get_data_as_text(exif, "Exif.Image.DateTime");
206                         if (text) subsec = exif_get_data_as_text(exif, "Exif.Photo.SubSecTime");
207                         }
208                 if (subsec)
209                         {
210                         gchar *tmp = text;
211                         text = g_strconcat(tmp, ".", subsec, NULL);
212                         g_free(tmp);
213                         g_free(subsec);
214                         }
215                 return text;
216                 }
217         if (strcmp(key, "fShutterSpeed") == 0)
218                 {
219                 ExifRational *r;
220
221                 r = exif_get_rational(exif, "Exif.Photo.ExposureTime", NULL);
222                 if (r && r->num && r->den)
223                         {
224                         double n = (double)r->den / (double)r->num;
225                         return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "",
226                                                           n > 1.0 ? n : 1.0 / n);
227                         }
228                 r = exif_get_rational(exif, "Exif.Photo.ShutterSpeedValue", NULL);
229                 if (r && r->num  && r->den)
230                         {
231                         double n = pow(2.0, exif_rational_to_double(r, TRUE));
232
233                         /* Correct exposure time to avoid values like 1/91s (seen on Minolta DImage 7) */
234                         if (n > 1.0 && (int)n - ((int)(n/10))*10 == 1) n--;
235
236                         return g_strdup_printf("%s%.0fs", n > 1.0 ? "1/" : "",
237                                                           n > 1.0 ? floor(n) : 1.0 / n);        
238                         }
239                 return NULL;
240                 }
241         if (strcmp(key, "fAperture") == 0)
242                 {
243                 double n;
244
245                 n = exif_get_rational_as_double(exif, "Exif.Photo.FNumber");
246                 if (n == 0.0) n = exif_get_rational_as_double(exif, "Exif.Photo.ApertureValue");
247                 if (n == 0.0) return NULL;
248
249                 return g_strdup_printf("f/%.1f", n);
250                 }
251         if (strcmp(key, "fExposureBias") == 0)
252                 {
253                 ExifRational *r;
254                 gint sign;
255                 double n;
256
257                 r = exif_get_rational(exif, "Exif.Photo.ExposureBiasValue", &sign);
258                 if (!r) return NULL;
259
260                 n = exif_rational_to_double(r, sign);
261                 return g_strdup_printf("%+.1f", n);
262                 }
263         if (strcmp(key, "fFocalLength") == 0)
264                 {
265                 double n;
266
267                 n = exif_get_rational_as_double(exif, "Exif.Photo.FocalLength");
268                 if (n == 0.0) return NULL;
269                 return g_strdup_printf("%.0f mm", n);
270                 }
271         if (strcmp(key, "fFocalLength35mmFilm") == 0)
272                 {
273                 gint n;
274                 double f, c;
275
276                 if (exif_get_integer(exif, "Exif.Photo.FocalLengthIn35mmFilm", &n) && n != 0)
277                         {
278                         return g_strdup_printf("%d mm", n);
279                         }
280                         
281                 f = exif_get_rational_as_double(exif, "Exif.Photo.FocalLength");
282                 c = get_crop_factor(exif);
283                 
284                 if (f != 0.0 && c != 0.0)
285                         {
286                         return g_strdup_printf("%.0f mm", f * c);
287                         }
288
289                 return NULL;
290                 }
291         if (strcmp(key, "fISOSpeedRating") == 0)
292                 {
293                 gchar *text;
294
295                 text = exif_get_data_as_text(exif, "Exif.Photo.ISOSpeedRatings");
296                 /* kodak may set this instead */
297                 if (!text) text = exif_get_data_as_text(exif, "Exif.Photo.ExposureIndex");
298                 return text;
299                 }
300         if (strcmp(key, "fSubjectDistance") == 0)
301                 {
302                 ExifRational *r;
303                 gint sign;
304                 double n;
305
306                 r = exif_get_rational(exif, "Exif.Photo.SubjectDistance", &sign);
307                 if (!r) return NULL;
308
309                 if ((long)r->num == 0xffffffff) return g_strdup(_("infinity"));
310                 if ((long)r->num == 0) return g_strdup(_("unknown"));
311
312                 n = exif_rational_to_double(r, sign);
313                 if (n == 0.0) return _("unknown");
314                 return g_strdup_printf("%.3f m", n);
315                 }
316         if (strcmp(key, "fFlash") == 0)
317                 {
318                 /* grr, flash is a bitmask... */
319                 GString *string;
320                 gchar *text;
321                 gint n;
322                 gint v;
323
324                 if (!exif_get_integer(exif, "Exif.Photo.Flash", &n)) return NULL;
325
326                 /* Exif 2.1 only defines first 3 bits */
327                 if (n <= 0x07) return exif_get_data_as_text(exif, "Exif.Photo.Flash");
328
329                 /* must be Exif 2.2 */
330                 string = g_string_new("");
331
332                 /* flash fired (bit 0) */
333                 string = g_string_append(string, (n & 0x01) ? _("yes") : _("no"));
334
335                 /* flash mode (bits 3, 4) */
336                 v = (n >> 3) & 0x03;
337                 if (v) string = append_comma_text(string, _("mode:"));
338                 switch (v)
339                         {
340                         case 1:
341                                 string = g_string_append(string, _("on"));
342                                 break;
343                         case 2:
344                                 string = g_string_append(string, _("off"));
345                                 break;
346                         case 3:
347                                 string = g_string_append(string, _("auto"));
348                                 break;
349                         }
350
351                 /* return light (bits 1, 2) */
352                 v = (n >> 1) & 0x03;
353                 if (v == 2) string = append_comma_text(string, _("not detected by strobe"));
354                 if (v == 3) string = append_comma_text(string, _("detected by strobe"));
355
356                 /* we ignore flash function (bit 5) */
357
358                 /* red-eye (bit 6) */
359                 if ((n >> 5) & 0x01) string = append_comma_text(string, _("red-eye reduction"));
360
361                 text = string->str;
362                 g_string_free(string, FALSE);
363                 return text;
364                 }
365         if (strcmp(key, "fResolution") == 0)
366                 {
367                 ExifRational *rx, *ry;
368                 gchar *units;
369                 gchar *text;
370
371                 rx = exif_get_rational(exif, "Exif.Image.XResolution", NULL);
372                 ry = exif_get_rational(exif, "Exif.Image.YResolution", NULL);
373                 if (!rx || !ry) return NULL;
374
375                 units = exif_get_data_as_text(exif, "Exif.Image.ResolutionUnit");
376                 text = g_strdup_printf("%0.f x %0.f (%s/%s)", rx->den ? (double)rx->num / rx->den : 1.0,
377                                                               ry->den ? (double)ry->num / ry->den : 1.0,
378                                                               _("dot"), (units) ? units : _("unknown"));
379
380                 g_free(units);
381                 return text;
382                 }
383
384         if (key_valid) *key_valid = FALSE;
385         return NULL;
386 }
387
388 const gchar *exif_get_description_by_key(const gchar *key)
389 {
390         gint i;
391
392         if (!key) return NULL;
393
394         i = 0;
395         while (ExifFormattedList[i].key != NULL)
396                 {
397                 if (strcmp(key, ExifFormattedList[i].key) == 0) return _(ExifFormattedList[i].description);
398                 i++;
399                 }
400
401         return exif_get_tag_description_by_key(key);
402 }
403
404 gint exif_get_integer(ExifData *exif, const gchar *key, gint *value)
405 {
406         ExifItem *item;
407
408         item = exif_get_item(exif, key);
409         return exif_item_get_integer(item, value);
410 }
411
412 ExifRational *exif_get_rational(ExifData *exif, const gchar *key, gint *sign)
413 {
414         ExifItem *item;
415
416         item = exif_get_item(exif, key);
417         return exif_item_get_rational(item, sign);
418 }
419
420 gchar *exif_get_data_as_text(ExifData *exif, const gchar *key)
421 {
422         ExifItem *item;
423         gchar *text;
424         gint key_valid;
425
426         if (!key) return NULL;
427
428         text = exif_get_formatted_by_key(exif, key, &key_valid);
429         if (key_valid) return text;
430
431         item = exif_get_item(exif, key);
432         if (item) return exif_item_get_data_as_text(item);
433
434         return NULL;
435 }
436
437 ExifData *exif_read_fd(FileData *fd, gint parse_color_profile)
438 {
439         GList *work;
440         gchar *sidecar_path = NULL;
441
442         if (!fd) return NULL;
443
444         work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
445
446         if (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE))
447                 {
448                 while(work)
449                         {
450                         FileData *sfd = work->data;
451                         work = work->next;
452                         if (strcasecmp(sfd->extension, ".xmp") == 0)
453                                 {
454                                 sidecar_path = sfd->path;
455                                 break;
456                                 }
457                         }
458                 }
459
460
461         // FIXME: some caching would be nice
462         return exif_read(fd->path, sidecar_path, parse_color_profile);
463 }