[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / pictures / ExifParse.cpp
blobe27942b571fa28279a6a25e5d85b25d3fecb1556
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 //--------------------------------------------------------------------------
10 // Program to pull the EXIF information out of various types of digital
11 // images and present it in a reasonably consistent way
13 // Original code pulled from 'jhead' by Matthias Wandel (http://www.sentex.net/~mwandel/) - jhead
14 // Adapted for XBMC by DD.
15 //--------------------------------------------------------------------------
17 // Note: Jhead supports TAG_MAKER_NOTE exif field,
18 // but that is omitted for now - to make porting easier and addition smaller
20 #include "ExifParse.h"
22 #ifdef TARGET_WINDOWS
23 #include <windows.h>
24 #else
25 #include <memory.h>
26 #include <cstring>
27 #endif
29 #include <math.h>
30 #include <stdio.h>
32 #ifndef min
33 #define min(a,b) (a)>(b)?(b):(a)
34 #endif
35 #ifndef max
36 #define max(a,b) (a)<(b)?(b):(a)
37 #endif
40 // Prototypes for exif utility functions.
41 static void ErrNonfatal(const char* const msg, int a1, int a2);
43 #define DIR_ENTRY_ADDR(Start, Entry) ((Start)+2+12*(Entry))
46 //--------------------------------------------------------------------------
47 // Describes tag values
48 #define TAG_DESCRIPTION 0x010E
49 #define TAG_MAKE 0x010F
50 #define TAG_MODEL 0x0110
51 #define TAG_ORIENTATION 0x0112
52 #define TAG_X_RESOLUTION 0x011A // Not processed. Format rational64u (see http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.76/html/TagNames/EXIF.html)
53 #define TAG_Y_RESOLUTION 0x011B // Not processed. Format rational64u
54 #define TAG_RESOLUTION_UNIT 0x0128 // Not processed. Format int16u. Values: 1-none; 2-inches; 3-cm
55 #define TAG_SOFTWARE 0x0131
56 #define TAG_DATETIME 0x0132
57 #define TAG_THUMBNAIL_OFFSET 0x0201
58 #define TAG_THUMBNAIL_LENGTH 0x0202
59 #define TAG_Y_CB_CR_POS 0x0213 // Not processed. Format int16u. Values: 1-Centered; 2-Co-sited
60 #define TAG_EXPOSURETIME 0x829A
61 #define TAG_FNUMBER 0x829D
62 #define TAG_EXIF_OFFSET 0x8769
63 #define TAG_EXPOSURE_PROGRAM 0x8822
64 #define TAG_GPSINFO 0x8825
65 #define TAG_ISO_EQUIVALENT 0x8827
66 #define TAG_EXIF_VERSION 0x9000 // Not processed.
67 #define TAG_COMPONENT_CFG 0x9101 // Not processed.
68 #define TAG_DATETIME_ORIGINAL 0x9003
69 #define TAG_DATETIME_DIGITIZED 0x9004
70 #define TAG_SHUTTERSPEED 0x9201
71 #define TAG_APERTURE 0x9202
72 #define TAG_EXPOSURE_BIAS 0x9204
73 #define TAG_MAXAPERTURE 0x9205
74 #define TAG_SUBJECT_DISTANCE 0x9206
75 #define TAG_METERING_MODE 0x9207
76 #define TAG_LIGHT_SOURCE 0x9208
77 #define TAG_FLASH 0x9209
78 #define TAG_FOCALLENGTH 0x920A
79 #define TAG_MAKER_NOTE 0x927C // Not processed yet. Maybe in the future.
80 #define TAG_USERCOMMENT 0x9286
81 #define TAG_XP_COMMENT 0x9c9c
82 #define TAG_FLASHPIX_VERSION 0xA000 // Not processed.
83 #define TAG_COLOUR_SPACE 0xA001 // Not processed. Format int16u. Values: 1-RGB; 2-Adobe RGB 65535-Uncalibrated
84 #define TAG_EXIF_IMAGEWIDTH 0xa002
85 #define TAG_EXIF_IMAGELENGTH 0xa003
86 #define TAG_INTEROP_OFFSET 0xa005
87 #define TAG_FOCALPLANEXRES 0xa20E
88 #define TAG_FOCALPLANEUNITS 0xa210
89 #define TAG_EXPOSURE_INDEX 0xa215
90 #define TAG_EXPOSURE_MODE 0xa402
91 #define TAG_WHITEBALANCE 0xa403
92 #define TAG_DIGITALZOOMRATIO 0xA404
93 #define TAG_FOCALLENGTH_35MM 0xa405
95 #define TAG_GPS_LAT_REF 1
96 #define TAG_GPS_LAT 2
97 #define TAG_GPS_LONG_REF 3
98 #define TAG_GPS_LONG 4
99 #define TAG_GPS_ALT_REF 5
100 #define TAG_GPS_ALT 6
102 //--------------------------------------------------------------------------
103 // Exif format descriptor stuff
104 namespace
106 constexpr auto FMT_BYTE = 1;
107 constexpr auto FMT_USHORT = 2;
108 constexpr auto FMT_ULONG = 3;
109 constexpr auto FMT_URATIONAL = 4;
110 constexpr auto FMT_SBYTE = 5;
111 constexpr auto FMT_SSHORT = 6;
112 constexpr auto FMT_SLONG = 7;
113 constexpr auto FMT_SRATIONAL = 8;
114 constexpr auto FMT_SINGLE = 9;
115 constexpr auto FMT_DOUBLE = 10;
116 // NOTE: Remember to change NUM_FORMATS if you define a new format
117 constexpr auto NUM_FORMATS = 10;
119 const unsigned int BytesPerFormat[NUM_FORMATS] = {1, 2, 4, 8, 1, 2, 4, 8, 4, 8};
120 } // namespace
122 //--------------------------------------------------------------------------
123 // Internationalisation string IDs. The enum order must match that in the
124 // language file (e.g. 'language/resource.language.en_gb/strings.po', and EXIF_PARSE_STRING_ID_BASE
125 // must match the ID of the first Exif string in that file.
126 #define EXIF_PARSE_STRING_ID_BASE 21800
127 enum {
128 // Distance
129 ExifStrDistanceInfinite = EXIF_PARSE_STRING_ID_BASE,
130 // Whitebalance et.al.
131 ExifStrManual,
132 ExifStrAuto,
133 // Flash modes
134 ExifStrYes,
135 ExifStrNo,
136 ExifStrFlashNoStrobe,
137 ExifStrFlashStrobe,
138 ExifStrFlashManual,
139 ExifStrFlashManualNoReturn,
140 ExifStrFlashManualReturn,
141 ExifStrFlashAuto,
142 ExifStrFlashAutoNoReturn,
143 ExifStrFlashAutoReturn,
144 ExifStrFlashRedEye,
145 ExifStrFlashRedEyeNoReturn,
146 ExifStrFlashRedEyeReturn,
147 ExifStrFlashManualRedEye,
148 ExifStrFlashManualRedEyeNoReturn,
149 ExifStrFlashManualRedEyeReturn,
150 ExifStrFlashAutoRedEye,
151 ExifStrFlashAutoRedEyeNoReturn,
152 ExifStrFlashAutoRedEyeReturn,
153 // Light sources
154 ExifStrDaylight,
155 ExifStrFluorescent,
156 ExifStrIncandescent,
157 ExifStrFlash,
158 ExifStrFineWeather,
159 ExifStrShade,
160 // Metering Mode
161 ExifStrMeteringCenter,
162 ExifStrMeteringSpot,
163 ExifStrMeteringMatrix,
164 // Exposure Program
165 ExifStrExposureProgram,
166 ExifStrExposureAperture,
167 ExifStrExposureShutter,
168 ExifStrExposureCreative,
169 ExifStrExposureAction,
170 ExifStrExposurePortrait,
171 ExifStrExposureLandscape,
172 // Exposure mode
173 ExifStrExposureModeAuto,
174 // ISO equivalent
175 ExifStrIsoEquivalent,
176 // GPS latitude, longitude, altitude
177 ExifStrGpsLatitude,
178 ExifStrGpsLongitude,
179 ExifStrGpsAltitude,
185 //--------------------------------------------------------------------------
186 // Report non fatal errors. Now that microsoft.net modifies exif headers,
187 // there's corrupted ones, and there could be more in the future.
188 //--------------------------------------------------------------------------
189 static void ErrNonfatal(const char* const msg, int a1, int a2)
191 printf("ExifParse - Nonfatal Error : %s %d %d", msg, a1, a2);
194 //--------------------------------------------------------------------------
195 // Convert a 16 bit unsigned value from file's native byte order
196 //--------------------------------------------------------------------------
197 int CExifParse::Get16(const void* const Short, const bool motorolaOrder)
199 if (motorolaOrder) {
200 return (((const unsigned char *)Short)[0] << 8) | ((const unsigned char *)Short)[1];
201 } else {
202 return (((const unsigned char *)Short)[1] << 8) | ((const unsigned char *)Short)[0];
206 //--------------------------------------------------------------------------
207 // Convert a 32 bit signed value from file's native byte order
208 //--------------------------------------------------------------------------
209 int CExifParse::Get32(const void* const Long, const bool motorolaOrder)
211 if (motorolaOrder) {
212 return (((const char *)Long)[0] << 24) | (((const unsigned char *)Long)[1] << 16)
213 | (((const unsigned char *)Long)[2] << 8 ) | (((const unsigned char *)Long)[3] << 0 );
214 } else {
215 return (((const char *)Long)[3] << 24) | (((const unsigned char *)Long)[2] << 16)
216 | (((const unsigned char *)Long)[1] << 8 ) | (((const unsigned char *)Long)[0] << 0 );
220 //--------------------------------------------------------------------------
221 // It appears that CStdString constructor replaces "\n" with "\r\n" which results
222 // in "\r\r\n" if there already is "\r\n", which in turn results in corrupted
223 // display. So this is an attempt to undo effects of a smart constructor. Also,
224 // replaces all nonprintable characters with "."
225 //--------------------------------------------------------------------------
226 /*void CExifParse::FixComment(CStdString& comment)
228 comment.Replace("\r\r\n", "\r\n");
229 for (unsigned int i=0; i<comment.length(); i++)
231 if ((comment[i] < 32) && (comment[i] != '\n') && (comment[i] != '\t') && (comment[i] != '\r'))
233 comment[i] = '.';
238 //--------------------------------------------------------------------------
239 // Evaluate number, be it int, rational, or float from directory.
240 //--------------------------------------------------------------------------
241 double CExifParse::ConvertAnyFormat(const void* const ValuePtr, int Format)
243 double Value;
244 Value = 0;
246 switch(Format)
248 case FMT_SBYTE: Value = *(const signed char*)ValuePtr; break;
249 case FMT_BYTE: Value = *(const unsigned char*)ValuePtr; break;
251 case FMT_USHORT: Value = Get16(ValuePtr, m_MotorolaOrder); break;
252 case FMT_ULONG: Value = (unsigned)Get32(ValuePtr, m_MotorolaOrder); break;
254 case FMT_URATIONAL:
255 case FMT_SRATIONAL:
257 int Num,Den;
258 Num = Get32(ValuePtr, m_MotorolaOrder);
259 Den = Get32(4+(const char *)ValuePtr, m_MotorolaOrder);
261 if (Den == 0) Value = 0;
262 else Value = (double)Num/Den;
264 break;
266 case FMT_SSHORT: Value = (signed short)Get16(ValuePtr, m_MotorolaOrder); break;
267 case FMT_SLONG: Value = Get32(ValuePtr, m_MotorolaOrder); break;
269 // Not sure if this is correct (never seen float used in Exif format)
270 case FMT_SINGLE: Value = (double)*(const float*)ValuePtr; break;
271 case FMT_DOUBLE: Value = *(const double*)ValuePtr; break;
273 default:
274 ErrNonfatal("Illegal format code %d",Format,0);
276 return Value;
279 //--------------------------------------------------------------------------
280 // Exif date tag is stored as a fixed format string "YYYY:MM:DD HH:MM:SS".
281 // If date is not set, then the string is filled with blanks and colons:
282 // " : : : : ". We want this string localised.
283 //--------------------------------------------------------------------------
284 /*void CExifParse::LocaliseDate (void)
286 if (m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME][0] != ' ')
288 int year = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(0, 4).c_str());
289 int month = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(5, 2).c_str());
290 int day = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(8, 2).c_str());
291 int hour = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(11,2).c_str());
292 int min = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(14,2).c_str());
293 int sec = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(17,2).c_str());
294 CDateTime date(year, month, day, hour, min, sec);
295 m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME] = date.GetAsLocalizedDateTime();
300 //--------------------------------------------------------------------------
301 // Convert exposure time into a human readable format
302 //--------------------------------------------------------------------------
303 /*void CExifParse::GetExposureTime(const float exposureTime, CStdString& outStr)
305 if (exposureTime)
307 if (exposureTime < 0.010) outStr.Format("%6.4fs ", exposureTime);
308 else outStr.Format("%5.3fs ", exposureTime);
309 if (exposureTime <= 0.5) outStr.Format("%s (1/%d)", outStr, (int)(0.5 + 1/exposureTime));
313 //--------------------------------------------------------------------------
314 // Process one of the nested EXIF directories.
315 //--------------------------------------------------------------------------
316 void CExifParse::ProcessDir(const unsigned char* const DirStart,
317 const unsigned char* const OffsetBase,
318 const unsigned ExifLength,
319 int NestingLevel)
321 if (NestingLevel > 4)
323 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
324 return;
327 char IndentString[25];
328 memset(IndentString, ' ', 25);
329 IndentString[NestingLevel * 4] = '\0';
332 int NumDirEntries = Get16((const void*)DirStart, m_MotorolaOrder);
334 const unsigned char* const DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
335 if (DirEnd+4 > (OffsetBase+ExifLength))
337 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength)
339 // Version 1.3 of jhead would truncate a bit too much.
340 // This also caught later on as well.
342 else
344 ErrNonfatal("Illegally sized directory", 0,0);
345 return;
349 for (int de=0;de<NumDirEntries;de++)
351 int Tag, Format, Components;
352 unsigned char* ValuePtr;
353 int ByteCount;
354 const unsigned char* const DirEntry = DIR_ENTRY_ADDR(DirStart, de);
356 Tag = Get16(DirEntry, m_MotorolaOrder);
357 Format = Get16(DirEntry+2, m_MotorolaOrder);
358 Components = Get32(DirEntry+4, m_MotorolaOrder);
360 if (Format <= 0 || Format > NUM_FORMATS)
362 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
363 continue;
366 if ((unsigned)Components > 0x10000)
368 ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
369 continue;
372 ByteCount = Components * BytesPerFormat[Format - 1];
374 if (ByteCount > 4)
376 unsigned OffsetVal;
377 OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
378 // If its bigger than 4 bytes, the dir entry contains an offset.
379 if (OffsetVal+ByteCount > ExifLength)
381 // Bogus pointer offset and / or bytecount value
382 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
383 continue;
385 ValuePtr = (unsigned char*)(const_cast<unsigned char*>(OffsetBase)+OffsetVal);
387 if (OffsetVal > m_LargestExifOffset)
389 m_LargestExifOffset = OffsetVal;
393 else {
394 // 4 bytes or less and value is in the dir entry itself
395 ValuePtr = (unsigned char*)(const_cast<unsigned char*>(DirEntry)+8);
399 // Extract useful components of tag
400 switch(Tag)
402 case TAG_DESCRIPTION:
404 int length = max(ByteCount, 0);
405 length = min(length, MAX_COMMENT);
406 strncpy(m_ExifInfo->Description, (char *)ValuePtr, length);
407 m_ExifInfo->Description[length] = '\0';
408 break;
410 case TAG_MAKE:
412 int space = sizeof(m_ExifInfo->CameraMake);
413 if (space > 0)
415 strncpy(m_ExifInfo->CameraMake, (char *)ValuePtr, space - 1);
416 m_ExifInfo->CameraMake[space - 1] = '\0';
418 break;
420 case TAG_MODEL:
422 int space = sizeof(m_ExifInfo->CameraModel);
423 if (space > 0)
425 strncpy(m_ExifInfo->CameraModel, (char *)ValuePtr, space - 1);
426 m_ExifInfo->CameraModel[space - 1] = '\0';
428 break;
430 // case TAG_SOFTWARE: strncpy(m_ExifInfo->Software, ValuePtr, 5); break;
431 case TAG_FOCALPLANEXRES: m_FocalPlaneXRes = ConvertAnyFormat(ValuePtr, Format); break;
432 case TAG_THUMBNAIL_OFFSET: m_ExifInfo->ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
433 case TAG_THUMBNAIL_LENGTH: m_ExifInfo->ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
435 case TAG_MAKER_NOTE:
436 continue;
437 break;
439 case TAG_DATETIME_ORIGINAL:
442 int space = sizeof(m_ExifInfo->DateTime);
443 if (space > 0)
445 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
446 m_ExifInfo->DateTime[space - 1] = '\0';
447 // If we get a DATETIME_ORIGINAL, we use that one.
448 m_DateFound = true;
450 break;
452 case TAG_DATETIME_DIGITIZED:
453 case TAG_DATETIME:
455 if (!m_DateFound)
457 // If we don't already have a DATETIME_ORIGINAL, use whatever
458 // time fields we may have.
459 int space = sizeof(m_ExifInfo->DateTime);
460 if (space > 0)
462 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
463 m_ExifInfo->DateTime[space - 1] = '\0';
466 break;
468 case TAG_USERCOMMENT:
470 // The UserComment allows comments without the charset limitations of ImageDescription.
471 // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
472 // - ASCII: 'ASCII\0\0\0'
473 // - Unicode: 'UNICODE\0'
474 // - JIS X208-1990: 'JIS\0\0\0\0\0'
475 // - Unknown: '\0\0\0\0\0\0\0\0' (application specific)
477 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNKNOWN;
479 const int EXIF_COMMENT_CHARSET_LENGTH = 8;
480 if (ByteCount >= EXIF_COMMENT_CHARSET_LENGTH)
482 // As some implementations use spaces instead of \0 for the padding,
483 // we're not so strict and check only the prefix.
484 if (memcmp(ValuePtr, "ASCII", 5) == 0)
485 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_ASCII;
486 else if (memcmp(ValuePtr, "UNICODE", 7) == 0)
487 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
488 else if (memcmp(ValuePtr, "JIS", 3) == 0)
489 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_JIS;
491 int length = ByteCount - EXIF_COMMENT_CHARSET_LENGTH;
492 length = min(length, MAX_COMMENT);
493 memcpy(m_ExifInfo->Comments, ValuePtr + EXIF_COMMENT_CHARSET_LENGTH, length);
494 m_ExifInfo->Comments[length] = '\0';
495 // FixComment(comment); // Ensure comment is printable
498 break;
500 case TAG_XP_COMMENT:
502 // The XP user comment field is always unicode (UCS-2) encoded
503 m_ExifInfo->XPCommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
504 size_t length = min(ByteCount, MAX_COMMENT);
505 memcpy(m_ExifInfo->XPComment, ValuePtr, length);
506 m_ExifInfo->XPComment[length] = '\0';
508 break;
510 case TAG_FNUMBER:
511 // Simplest way of expressing aperture, so I trust it the most.
512 // (overwrite previously computd value if there is one)
513 m_ExifInfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
514 break;
516 case TAG_APERTURE:
517 case TAG_MAXAPERTURE:
518 // More relevant info always comes earlier, so only use this field if we don't
519 // have appropriate aperture information yet.
520 if (m_ExifInfo->ApertureFNumber == 0)
522 m_ExifInfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
524 break;
526 case TAG_FOCALLENGTH:
527 // Nice digital cameras actually save the focal length as a function
528 // of how far they are zoomed in.
529 m_ExifInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
530 break;
532 case TAG_SUBJECT_DISTANCE:
533 // Inidcates the distacne the autofocus camera is focused to.
534 // Tends to be less accurate as distance increases.
536 float distance = (float)ConvertAnyFormat(ValuePtr, Format);
537 m_ExifInfo->Distance = distance;
539 break;
541 case TAG_EXPOSURETIME:
543 // Simplest way of expressing exposure time, so I trust it most.
544 // (overwrite previously computd value if there is one)
545 float expTime = (float)ConvertAnyFormat(ValuePtr, Format);
546 if (expTime)
547 m_ExifInfo->ExposureTime = expTime;
549 break;
551 case TAG_SHUTTERSPEED:
552 // More complicated way of expressing exposure time, so only use
553 // this value if we don't already have it from somewhere else.
554 if (m_ExifInfo->ExposureTime == 0)
556 m_ExifInfo->ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
558 break;
560 case TAG_FLASH:
561 m_ExifInfo->FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
562 break;
564 case TAG_ORIENTATION:
565 m_ExifInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
566 if (m_ExifInfo->Orientation < 0 || m_ExifInfo->Orientation > 8)
568 ErrNonfatal("Undefined rotation value %d", m_ExifInfo->Orientation, 0);
569 m_ExifInfo->Orientation = 0;
571 break;
573 case TAG_EXIF_IMAGELENGTH:
574 case TAG_EXIF_IMAGEWIDTH:
575 // Use largest of height and width to deal with images that have been
576 // rotated to portrait format.
578 int a = (int)ConvertAnyFormat(ValuePtr, Format);
579 if (m_ExifImageWidth < a) m_ExifImageWidth = a;
581 break;
583 case TAG_FOCALPLANEUNITS:
584 switch((int)ConvertAnyFormat(ValuePtr, Format))
586 // According to the information I was using, 2 means meters.
587 // But looking at the Cannon powershot's files, inches is the only
588 // sensible value.
589 case 1: m_FocalPlaneUnits = 25.4; break; // inch
590 case 2: m_FocalPlaneUnits = 25.4; break;
591 case 3: m_FocalPlaneUnits = 10; break; // centimeter
592 case 4: m_FocalPlaneUnits = 1; break; // millimeter
593 case 5: m_FocalPlaneUnits = .001; break; // micrometer
595 break;
597 case TAG_EXPOSURE_BIAS:
598 m_ExifInfo->ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
599 break;
601 case TAG_WHITEBALANCE:
602 m_ExifInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
603 break;
605 case TAG_LIGHT_SOURCE:
606 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
607 m_ExifInfo->LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
608 break;
610 case TAG_METERING_MODE:
611 m_ExifInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
612 break;
614 case TAG_EXPOSURE_PROGRAM:
615 m_ExifInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
616 break;
618 case TAG_EXPOSURE_INDEX:
619 if (m_ExifInfo->ISOequivalent == 0)
621 // Exposure index and ISO equivalent are often used interchangeably,
622 // so we will do the same.
623 // http://photography.about.com/library/glossary/bldef_ei.htm
624 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
626 break;
628 case TAG_ISO_EQUIVALENT:
629 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
630 if (m_ExifInfo->ISOequivalent < 50)
631 m_ExifInfo->ISOequivalent *= 200; // Fixes strange encoding on some older digicams.
632 break;
634 case TAG_EXPOSURE_MODE:
635 m_ExifInfo->ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
636 break;
638 case TAG_DIGITALZOOMRATIO:
639 m_ExifInfo->DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
640 break;
642 case TAG_EXIF_OFFSET:
643 case TAG_INTEROP_OFFSET:
645 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
646 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
648 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
650 else
652 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
654 continue;
656 break;
658 case TAG_GPSINFO:
660 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
661 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
663 ErrNonfatal("Illegal GPS directory link",0,0);
665 else
667 ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
669 continue;
671 break;
673 case TAG_FOCALLENGTH_35MM:
674 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
675 // if its present, use it to compute equivalent focal length instead of
676 // computing it from sensor geometry and actual focal length.
677 m_ExifInfo->FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
678 break;
683 // In addition to linking to subdirectories via exif tags,
684 // there's also a potential link to another directory at the end of each
685 // directory. this has got to be the result of a committee!
686 unsigned Offset;
688 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength)
690 Offset = (unsigned)Get32(DirStart+2+12*NumDirEntries, m_MotorolaOrder);
691 if (Offset)
693 const unsigned char* const SubdirStart = OffsetBase + Offset;
694 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase)
696 if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20)
698 // Jhead 1.3 or earlier would crop the whole directory!
699 // As Jhead produces this form of format incorrectness,
700 // I'll just let it pass silently
702 else
704 ErrNonfatal("Illegal subdirectory link",0,0);
707 else
709 if (SubdirStart <= OffsetBase+ExifLength)
711 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
714 if (Offset > m_LargestExifOffset)
716 m_LargestExifOffset = Offset;
720 else
722 // The exif header ends before the last next directory pointer.
725 if (m_ExifInfo->ThumbnailOffset)
727 m_ExifInfo->ThumbnailAtEnd = false;
729 if (m_ExifInfo->ThumbnailOffset <= ExifLength)
731 if (m_ExifInfo->ThumbnailSize > ExifLength - m_ExifInfo->ThumbnailOffset)
733 // If thumbnail extends past exif header, only save the part that
734 // actually exists. Canon's EOS viewer utility will do this - the
735 // thumbnail extracts ok with this hack.
736 m_ExifInfo->ThumbnailSize = ExifLength - m_ExifInfo->ThumbnailOffset;
743 //--------------------------------------------------------------------------
744 // Process a EXIF marker
745 // Describes all the drivel that most digital cameras include...
746 //--------------------------------------------------------------------------
747 bool CExifParse::Process (const unsigned char* const ExifSection, const unsigned short length, ExifInfo_t *info)
749 m_ExifInfo = info;
750 // EXIF signature: "Exif\0\0"
751 // Check EXIF signatures
752 const char ExifHeader[] = "Exif\0\0";
753 const char ExifAlignment0[] = "II";
754 const char ExifAlignment1[] = "MM";
755 const char ExifExtra = 0x2a;
757 const char* pos = (const char*)(ExifSection + sizeof(short)); // position data pointer after length field
759 if (memcmp(pos, ExifHeader,6))
761 printf("ExifParse: incorrect Exif header");
762 return false;
764 pos += 6;
766 if (memcmp(pos, ExifAlignment0, strlen(ExifAlignment0)) == 0)
768 m_MotorolaOrder = false;
770 else if (memcmp(pos, ExifAlignment1, strlen(ExifAlignment1)) == 0)
772 m_MotorolaOrder = true;
774 else
776 printf("ExifParse: invalid Exif alignment marker");
777 return false;
779 pos += strlen(ExifAlignment0);
781 // Check the next value for correctness.
782 if (Get16((const void*)(pos), m_MotorolaOrder) != ExifExtra)
784 printf("ExifParse: invalid Exif start (1)");
785 return false;
787 pos += sizeof(short);
789 unsigned long FirstOffset = (unsigned)Get32((const void*)pos, m_MotorolaOrder);
790 if (FirstOffset < 8 || FirstOffset > 16)
792 // Usually set to 8, but other values valid too.
793 // CLog::Log(LOGERROR, "ExifParse: suspicious offset of first IFD value");
798 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
799 ProcessDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
801 m_ExifInfo->ThumbnailAtEnd = m_ExifInfo->ThumbnailOffset >= m_LargestExifOffset;
803 // Compute the CCD width, in millimeters.
804 if (m_FocalPlaneXRes != 0)
806 // Note: With some cameras, its not possible to compute this correctly because
807 // they don't adjust the indicated focal plane resolution units when using less
808 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
809 // that Jhead can do about it - its a camera problem.
810 m_ExifInfo->CCDWidth = (float)(m_ExifImageWidth * m_FocalPlaneUnits / m_FocalPlaneXRes);
813 if (m_ExifInfo->FocalLength)
815 if (m_ExifInfo->FocalLength35mmEquiv == 0)
817 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
818 // already got it explicitly from a tag.
819 if (m_ExifInfo->CCDWidth != 0.0f)
821 m_ExifInfo->FocalLength35mmEquiv =
822 (int)(m_ExifInfo->FocalLength / m_ExifInfo->CCDWidth * 36 + 0.5f);
826 return true;
831 //--------------------------------------------------------------------------
832 // GPS Lat/Long extraction helper function
833 //--------------------------------------------------------------------------
834 void CExifParse::GetLatLong(
835 const unsigned int Format,
836 const unsigned char* ValuePtr,
837 const int ComponentSize,
838 char *latLongString)
840 if (Format != FMT_URATIONAL)
842 ErrNonfatal("Illegal number format %d for GPS Lat/Long", Format, 0);
844 else
846 double Values[3];
847 for (unsigned a=0; a<3 ;a++)
849 Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
851 if (Values[0] < 0 || Values[0] > 180 || Values[1] < 0 || Values[1] >= 60 || Values[2] < 0 || Values[2] >= 60)
853 // Ignore invalid values (DMS format expected)
854 ErrNonfatal("Invalid Lat/Long value", 0, 0);
855 latLongString[0] = 0;
857 else
859 char latLong[30];
860 sprintf(latLong, "%3.0fd %2.0f' %5.2f\"", Values[0], Values[1], Values[2]);
861 strcat(latLongString, latLong);
866 //--------------------------------------------------------------------------
867 // Process GPS info directory
868 //--------------------------------------------------------------------------
869 void CExifParse::ProcessGpsInfo(
870 const unsigned char* const DirStart,
871 int ByteCountUnused,
872 const unsigned char* const OffsetBase,
873 unsigned ExifLength)
875 int NumDirEntries = Get16(DirStart, m_MotorolaOrder);
877 for (int de=0;de<NumDirEntries;de++)
879 const unsigned char* DirEntry = DIR_ENTRY_ADDR(DirStart, de);
881 unsigned Tag = Get16(DirEntry, m_MotorolaOrder);
882 unsigned Format = Get16(DirEntry+2, m_MotorolaOrder);
883 unsigned Components = (unsigned)Get32(DirEntry+4, m_MotorolaOrder);
884 if (Format == 0 || Format > NUM_FORMATS)
886 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
887 continue;
890 unsigned ComponentSize = BytesPerFormat[Format - 1];
891 unsigned ByteCount = Components * ComponentSize;
893 const unsigned char* ValuePtr;
895 if (ByteCount > 4)
897 unsigned OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
898 // If its bigger than 4 bytes, the dir entry contains an offset.
899 if (OffsetVal+ByteCount > ExifLength)
901 // Bogus pointer offset and / or bytecount value
902 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
903 continue;
905 ValuePtr = OffsetBase+OffsetVal;
907 else
909 // 4 bytes or less and value is in the dir entry itself
910 ValuePtr = DirEntry+8;
913 switch(Tag)
915 case TAG_GPS_LAT_REF:
916 m_ExifInfo->GpsLat[0] = ValuePtr[0];
917 m_ExifInfo->GpsLat[1] = 0;
918 break;
920 case TAG_GPS_LONG_REF:
921 m_ExifInfo->GpsLong[0] = ValuePtr[0];
922 m_ExifInfo->GpsLong[1] = 0;
923 break;
925 case TAG_GPS_LAT:
926 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLat);
927 break;
928 case TAG_GPS_LONG:
929 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLong);
930 break;
932 case TAG_GPS_ALT_REF:
933 if (ValuePtr[0] != 0)
934 m_ExifInfo->GpsAlt[0] = '-';
935 m_ExifInfo->GpsAlt[1] = 0;
936 break;
938 case TAG_GPS_ALT:
940 char temp[18];
941 sprintf(temp, "%.2fm", static_cast<double>(ConvertAnyFormat(ValuePtr, Format)));
942 strcat(m_ExifInfo->GpsAlt, temp);
944 break;