[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / pictures / ExifParse.cpp
blob6950559f10b36c015fc142e93929630f8af279f3
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 <stdint.h>
31 #include <stdio.h>
33 #ifndef min
34 #define min(a,b) (a)>(b)?(b):(a)
35 #endif
36 #ifndef max
37 #define max(a,b) (a)<(b)?(b):(a)
38 #endif
41 // Prototypes for exif utility functions.
42 static void ErrNonfatal(const char* const msg, int a1, int a2);
44 #define DIR_ENTRY_ADDR(Start, Entry) ((Start)+2+12*(Entry))
47 //--------------------------------------------------------------------------
48 // Describes tag values
49 #define TAG_DESCRIPTION 0x010E
50 #define TAG_MAKE 0x010F
51 #define TAG_MODEL 0x0110
52 #define TAG_ORIENTATION 0x0112
53 #define TAG_X_RESOLUTION 0x011A // Not processed. Format rational64u (see http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.76/html/TagNames/EXIF.html)
54 #define TAG_Y_RESOLUTION 0x011B // Not processed. Format rational64u
55 #define TAG_RESOLUTION_UNIT 0x0128 // Not processed. Format int16u. Values: 1-none; 2-inches; 3-cm
56 #define TAG_SOFTWARE 0x0131
57 #define TAG_DATETIME 0x0132
58 #define TAG_THUMBNAIL_OFFSET 0x0201
59 #define TAG_THUMBNAIL_LENGTH 0x0202
60 #define TAG_Y_CB_CR_POS 0x0213 // Not processed. Format int16u. Values: 1-Centered; 2-Co-sited
61 #define TAG_EXPOSURETIME 0x829A
62 #define TAG_FNUMBER 0x829D
63 #define TAG_EXIF_OFFSET 0x8769
64 #define TAG_EXPOSURE_PROGRAM 0x8822
65 #define TAG_GPSINFO 0x8825
66 #define TAG_ISO_EQUIVALENT 0x8827
67 #define TAG_EXIF_VERSION 0x9000 // Not processed.
68 #define TAG_COMPONENT_CFG 0x9101 // Not processed.
69 #define TAG_DATETIME_ORIGINAL 0x9003
70 #define TAG_DATETIME_DIGITIZED 0x9004
71 #define TAG_SHUTTERSPEED 0x9201
72 #define TAG_APERTURE 0x9202
73 #define TAG_EXPOSURE_BIAS 0x9204
74 #define TAG_MAXAPERTURE 0x9205
75 #define TAG_SUBJECT_DISTANCE 0x9206
76 #define TAG_METERING_MODE 0x9207
77 #define TAG_LIGHT_SOURCE 0x9208
78 #define TAG_FLASH 0x9209
79 #define TAG_FOCALLENGTH 0x920A
80 #define TAG_MAKER_NOTE 0x927C // Not processed yet. Maybe in the future.
81 #define TAG_USERCOMMENT 0x9286
82 #define TAG_XP_COMMENT 0x9c9c
83 #define TAG_FLASHPIX_VERSION 0xA000 // Not processed.
84 #define TAG_COLOUR_SPACE 0xA001 // Not processed. Format int16u. Values: 1-RGB; 2-Adobe RGB 65535-Uncalibrated
85 #define TAG_EXIF_IMAGEWIDTH 0xa002
86 #define TAG_EXIF_IMAGELENGTH 0xa003
87 #define TAG_INTEROP_OFFSET 0xa005
88 #define TAG_FOCALPLANEXRES 0xa20E
89 #define TAG_FOCALPLANEUNITS 0xa210
90 #define TAG_EXPOSURE_INDEX 0xa215
91 #define TAG_EXPOSURE_MODE 0xa402
92 #define TAG_WHITEBALANCE 0xa403
93 #define TAG_DIGITALZOOMRATIO 0xA404
94 #define TAG_FOCALLENGTH_35MM 0xa405
96 #define TAG_GPS_LAT_REF 1
97 #define TAG_GPS_LAT 2
98 #define TAG_GPS_LONG_REF 3
99 #define TAG_GPS_LONG 4
100 #define TAG_GPS_ALT_REF 5
101 #define TAG_GPS_ALT 6
103 //--------------------------------------------------------------------------
104 // Exif format descriptor stuff
105 namespace
107 constexpr auto FMT_BYTE = 1;
108 constexpr auto FMT_USHORT = 2;
109 constexpr auto FMT_ULONG = 3;
110 constexpr auto FMT_URATIONAL = 4;
111 constexpr auto FMT_SBYTE = 5;
112 constexpr auto FMT_SSHORT = 6;
113 constexpr auto FMT_SLONG = 7;
114 constexpr auto FMT_SRATIONAL = 8;
115 constexpr auto FMT_SINGLE = 9;
116 constexpr auto FMT_DOUBLE = 10;
117 // NOTE: Remember to change NUM_FORMATS if you define a new format
118 constexpr auto NUM_FORMATS = 10;
120 const unsigned int BytesPerFormat[NUM_FORMATS] = {1, 2, 4, 8, 1, 2, 4, 8, 4, 8};
121 } // namespace
123 //--------------------------------------------------------------------------
124 // Internationalisation string IDs. The enum order must match that in the
125 // language file (e.g. 'language/resource.language.en_gb/strings.po', and EXIF_PARSE_STRING_ID_BASE
126 // must match the ID of the first Exif string in that file.
127 #define EXIF_PARSE_STRING_ID_BASE 21800
128 enum {
129 // Distance
130 ExifStrDistanceInfinite = EXIF_PARSE_STRING_ID_BASE,
131 // Whitebalance et.al.
132 ExifStrManual,
133 ExifStrAuto,
134 // Flash modes
135 ExifStrYes,
136 ExifStrNo,
137 ExifStrFlashNoStrobe,
138 ExifStrFlashStrobe,
139 ExifStrFlashManual,
140 ExifStrFlashManualNoReturn,
141 ExifStrFlashManualReturn,
142 ExifStrFlashAuto,
143 ExifStrFlashAutoNoReturn,
144 ExifStrFlashAutoReturn,
145 ExifStrFlashRedEye,
146 ExifStrFlashRedEyeNoReturn,
147 ExifStrFlashRedEyeReturn,
148 ExifStrFlashManualRedEye,
149 ExifStrFlashManualRedEyeNoReturn,
150 ExifStrFlashManualRedEyeReturn,
151 ExifStrFlashAutoRedEye,
152 ExifStrFlashAutoRedEyeNoReturn,
153 ExifStrFlashAutoRedEyeReturn,
154 // Light sources
155 ExifStrDaylight,
156 ExifStrFluorescent,
157 ExifStrIncandescent,
158 ExifStrFlash,
159 ExifStrFineWeather,
160 ExifStrShade,
161 // Metering Mode
162 ExifStrMeteringCenter,
163 ExifStrMeteringSpot,
164 ExifStrMeteringMatrix,
165 // Exposure Program
166 ExifStrExposureProgram,
167 ExifStrExposureAperture,
168 ExifStrExposureShutter,
169 ExifStrExposureCreative,
170 ExifStrExposureAction,
171 ExifStrExposurePortrait,
172 ExifStrExposureLandscape,
173 // Exposure mode
174 ExifStrExposureModeAuto,
175 // ISO equivalent
176 ExifStrIsoEquivalent,
177 // GPS latitude, longitude, altitude
178 ExifStrGpsLatitude,
179 ExifStrGpsLongitude,
180 ExifStrGpsAltitude,
186 //--------------------------------------------------------------------------
187 // Report non fatal errors. Now that microsoft.net modifies exif headers,
188 // there's corrupted ones, and there could be more in the future.
189 //--------------------------------------------------------------------------
190 static void ErrNonfatal(const char* const msg, int a1, int a2)
192 printf("ExifParse - Nonfatal Error : %s %d %d", msg, a1, a2);
195 //--------------------------------------------------------------------------
196 // Convert a 16 bit unsigned value from file's native byte order
197 //--------------------------------------------------------------------------
198 int CExifParse::Get16(const void* const Short, const bool motorolaOrder)
200 if (motorolaOrder) {
201 return (((const unsigned char *)Short)[0] << 8) | ((const unsigned char *)Short)[1];
202 } else {
203 return (((const unsigned char *)Short)[1] << 8) | ((const unsigned char *)Short)[0];
207 //--------------------------------------------------------------------------
208 // Convert a 32 bit signed value from file's native byte order
209 //--------------------------------------------------------------------------
210 int CExifParse::Get32(const void* const Long, const bool motorolaOrder)
212 if (motorolaOrder) {
213 return (((const char *)Long)[0] << 24) | (((const unsigned char *)Long)[1] << 16)
214 | (((const unsigned char *)Long)[2] << 8 ) | (((const unsigned char *)Long)[3] << 0 );
215 } else {
216 return (((const char *)Long)[3] << 24) | (((const unsigned char *)Long)[2] << 16)
217 | (((const unsigned char *)Long)[1] << 8 ) | (((const unsigned char *)Long)[0] << 0 );
221 //--------------------------------------------------------------------------
222 // It appears that CStdString constructor replaces "\n" with "\r\n" which results
223 // in "\r\r\n" if there already is "\r\n", which in turn results in corrupted
224 // display. So this is an attempt to undo effects of a smart constructor. Also,
225 // replaces all nonprintable characters with "."
226 //--------------------------------------------------------------------------
227 /*void CExifParse::FixComment(CStdString& comment)
229 comment.Replace("\r\r\n", "\r\n");
230 for (unsigned int i=0; i<comment.length(); i++)
232 if ((comment[i] < 32) && (comment[i] != '\n') && (comment[i] != '\t') && (comment[i] != '\r'))
234 comment[i] = '.';
239 //--------------------------------------------------------------------------
240 // Evaluate number, be it int, rational, or float from directory.
241 //--------------------------------------------------------------------------
242 double CExifParse::ConvertAnyFormat(const void* const ValuePtr, int Format)
244 double Value;
245 Value = 0;
247 switch(Format)
249 case FMT_SBYTE: Value = *(const signed char*)ValuePtr; break;
250 case FMT_BYTE: Value = *(const unsigned char*)ValuePtr; break;
252 case FMT_USHORT: Value = Get16(ValuePtr, m_MotorolaOrder); break;
253 case FMT_ULONG: Value = (unsigned)Get32(ValuePtr, m_MotorolaOrder); break;
255 case FMT_URATIONAL:
256 case FMT_SRATIONAL:
258 int Num,Den;
259 Num = Get32(ValuePtr, m_MotorolaOrder);
260 Den = Get32(4+(const char *)ValuePtr, m_MotorolaOrder);
262 if (Den == 0) Value = 0;
263 else Value = (double)Num/Den;
265 break;
267 case FMT_SSHORT: Value = (signed short)Get16(ValuePtr, m_MotorolaOrder); break;
268 case FMT_SLONG: Value = Get32(ValuePtr, m_MotorolaOrder); break;
270 // Not sure if this is correct (never seen float used in Exif format)
271 case FMT_SINGLE: Value = (double)*(const float*)ValuePtr; break;
272 case FMT_DOUBLE: Value = *(const double*)ValuePtr; break;
274 default:
275 ErrNonfatal("Illegal format code %d",Format,0);
277 return Value;
280 //--------------------------------------------------------------------------
281 // Exif date tag is stored as a fixed format string "YYYY:MM:DD HH:MM:SS".
282 // If date is not set, then the string is filled with blanks and colons:
283 // " : : : : ". We want this string localised.
284 //--------------------------------------------------------------------------
285 /*void CExifParse::LocaliseDate (void)
287 if (m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME][0] != ' ')
289 int year = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(0, 4).c_str());
290 int month = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(5, 2).c_str());
291 int day = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(8, 2).c_str());
292 int hour = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(11,2).c_str());
293 int min = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(14,2).c_str());
294 int sec = atoi(m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME].substr(17,2).c_str());
295 CDateTime date(year, month, day, hour, min, sec);
296 m_ExifInfo[SLIDESHOW_EXIF_DATE_TIME] = date.GetAsLocalizedDateTime();
301 //--------------------------------------------------------------------------
302 // Convert exposure time into a human readable format
303 //--------------------------------------------------------------------------
304 /*void CExifParse::GetExposureTime(const float exposureTime, CStdString& outStr)
306 if (exposureTime)
308 if (exposureTime < 0.010) outStr.Format("%6.4fs ", exposureTime);
309 else outStr.Format("%5.3fs ", exposureTime);
310 if (exposureTime <= 0.5) outStr.Format("%s (1/%d)", outStr, (int)(0.5 + 1/exposureTime));
314 //--------------------------------------------------------------------------
315 // Process one of the nested EXIF directories.
316 //--------------------------------------------------------------------------
317 void CExifParse::ProcessDir(const unsigned char* const DirStart,
318 const unsigned char* const OffsetBase,
319 const unsigned ExifLength,
320 int NestingLevel)
322 if (NestingLevel > 4)
324 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
325 return;
328 char IndentString[25];
329 memset(IndentString, ' ', 25);
330 IndentString[NestingLevel * 4] = '\0';
333 int NumDirEntries = Get16((const void*)DirStart, m_MotorolaOrder);
335 const unsigned char* const DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
336 if (DirEnd+4 > (OffsetBase+ExifLength))
338 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength)
340 // Version 1.3 of jhead would truncate a bit too much.
341 // This also caught later on as well.
343 else
345 ErrNonfatal("Illegally sized directory", 0,0);
346 return;
350 for (int de=0;de<NumDirEntries;de++)
352 int Tag, Format, Components;
353 unsigned char* ValuePtr;
354 int ByteCount;
355 const unsigned char* const DirEntry = DIR_ENTRY_ADDR(DirStart, de);
357 Tag = Get16(DirEntry, m_MotorolaOrder);
358 Format = Get16(DirEntry+2, m_MotorolaOrder);
359 Components = Get32(DirEntry+4, m_MotorolaOrder);
361 if (Format <= 0 || Format > NUM_FORMATS)
363 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
364 continue;
367 if ((unsigned)Components > 0x10000)
369 ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
370 continue;
373 ByteCount = Components * BytesPerFormat[Format - 1];
375 if (ByteCount > 4)
377 unsigned OffsetVal;
378 OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
379 // If its bigger than 4 bytes, the dir entry contains an offset.
380 if (OffsetVal > UINT32_MAX - ByteCount || OffsetVal + ByteCount > ExifLength)
382 // Bogus pointer offset and / or bytecount value
383 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
384 continue;
386 ValuePtr = (unsigned char*)(const_cast<unsigned char*>(OffsetBase)+OffsetVal);
388 if (OffsetVal > m_LargestExifOffset)
390 m_LargestExifOffset = OffsetVal;
394 else {
395 // 4 bytes or less and value is in the dir entry itself
396 ValuePtr = (unsigned char*)(const_cast<unsigned char*>(DirEntry)+8);
400 // Extract useful components of tag
401 switch(Tag)
403 case TAG_DESCRIPTION:
405 int length = max(ByteCount, 0);
406 length = min(length, MAX_COMMENT);
407 strncpy(m_ExifInfo->Description, (char *)ValuePtr, length);
408 m_ExifInfo->Description[length] = '\0';
409 break;
411 case TAG_MAKE:
413 int space = sizeof(m_ExifInfo->CameraMake);
414 if (space > 0)
416 strncpy(m_ExifInfo->CameraMake, (char *)ValuePtr, space - 1);
417 m_ExifInfo->CameraMake[space - 1] = '\0';
419 break;
421 case TAG_MODEL:
423 int space = sizeof(m_ExifInfo->CameraModel);
424 if (space > 0)
426 strncpy(m_ExifInfo->CameraModel, (char *)ValuePtr, space - 1);
427 m_ExifInfo->CameraModel[space - 1] = '\0';
429 break;
431 // case TAG_SOFTWARE: strncpy(m_ExifInfo->Software, ValuePtr, 5); break;
432 case TAG_FOCALPLANEXRES: m_FocalPlaneXRes = ConvertAnyFormat(ValuePtr, Format); break;
433 case TAG_THUMBNAIL_OFFSET: m_ExifInfo->ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
434 case TAG_THUMBNAIL_LENGTH: m_ExifInfo->ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break;
436 case TAG_MAKER_NOTE:
437 continue;
438 break;
440 case TAG_DATETIME_ORIGINAL:
443 int space = sizeof(m_ExifInfo->DateTime);
444 if (space > 0)
446 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
447 m_ExifInfo->DateTime[space - 1] = '\0';
448 // If we get a DATETIME_ORIGINAL, we use that one.
449 m_DateFound = true;
451 break;
453 case TAG_DATETIME_DIGITIZED:
454 case TAG_DATETIME:
456 if (!m_DateFound)
458 // If we don't already have a DATETIME_ORIGINAL, use whatever
459 // time fields we may have.
460 int space = sizeof(m_ExifInfo->DateTime);
461 if (space > 0)
463 strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
464 m_ExifInfo->DateTime[space - 1] = '\0';
467 break;
469 case TAG_USERCOMMENT:
471 // The UserComment allows comments without the charset limitations of ImageDescription.
472 // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
473 // - ASCII: 'ASCII\0\0\0'
474 // - Unicode: 'UNICODE\0'
475 // - JIS X208-1990: 'JIS\0\0\0\0\0'
476 // - Unknown: '\0\0\0\0\0\0\0\0' (application specific)
478 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNKNOWN;
480 const int EXIF_COMMENT_CHARSET_LENGTH = 8;
481 if (ByteCount >= EXIF_COMMENT_CHARSET_LENGTH)
483 // As some implementations use spaces instead of \0 for the padding,
484 // we're not so strict and check only the prefix.
485 if (memcmp(ValuePtr, "ASCII", 5) == 0)
486 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_ASCII;
487 else if (memcmp(ValuePtr, "UNICODE", 7) == 0)
488 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
489 else if (memcmp(ValuePtr, "JIS", 3) == 0)
490 m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_JIS;
492 int length = ByteCount - EXIF_COMMENT_CHARSET_LENGTH;
493 length = min(length, MAX_COMMENT);
494 memcpy(m_ExifInfo->Comments, ValuePtr + EXIF_COMMENT_CHARSET_LENGTH, length);
495 m_ExifInfo->Comments[length] = '\0';
496 // FixComment(comment); // Ensure comment is printable
499 break;
501 case TAG_XP_COMMENT:
503 // The XP user comment field is always unicode (UCS-2) encoded
504 m_ExifInfo->XPCommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
505 size_t length = min(ByteCount, MAX_COMMENT);
506 memcpy(m_ExifInfo->XPComment, ValuePtr, length);
507 m_ExifInfo->XPComment[length] = '\0';
509 break;
511 case TAG_FNUMBER:
512 // Simplest way of expressing aperture, so I trust it the most.
513 // (overwrite previously computd value if there is one)
514 m_ExifInfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
515 break;
517 case TAG_APERTURE:
518 case TAG_MAXAPERTURE:
519 // More relevant info always comes earlier, so only use this field if we don't
520 // have appropriate aperture information yet.
521 if (m_ExifInfo->ApertureFNumber == 0)
523 m_ExifInfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
525 break;
527 case TAG_FOCALLENGTH:
528 // Nice digital cameras actually save the focal length as a function
529 // of how far they are zoomed in.
530 m_ExifInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
531 break;
533 case TAG_SUBJECT_DISTANCE:
534 // Inidcates the distacne the autofocus camera is focused to.
535 // Tends to be less accurate as distance increases.
537 float distance = (float)ConvertAnyFormat(ValuePtr, Format);
538 m_ExifInfo->Distance = distance;
540 break;
542 case TAG_EXPOSURETIME:
544 // Simplest way of expressing exposure time, so I trust it most.
545 // (overwrite previously computd value if there is one)
546 float expTime = (float)ConvertAnyFormat(ValuePtr, Format);
547 if (expTime)
548 m_ExifInfo->ExposureTime = expTime;
550 break;
552 case TAG_SHUTTERSPEED:
553 // More complicated way of expressing exposure time, so only use
554 // this value if we don't already have it from somewhere else.
555 if (m_ExifInfo->ExposureTime == 0)
557 m_ExifInfo->ExposureTime = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
559 break;
561 case TAG_FLASH:
562 m_ExifInfo->FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
563 break;
565 case TAG_ORIENTATION:
566 m_ExifInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
567 if (m_ExifInfo->Orientation < 0 || m_ExifInfo->Orientation > 8)
569 ErrNonfatal("Undefined rotation value %d", m_ExifInfo->Orientation, 0);
570 m_ExifInfo->Orientation = 0;
572 break;
574 case TAG_EXIF_IMAGELENGTH:
575 case TAG_EXIF_IMAGEWIDTH:
576 // Use largest of height and width to deal with images that have been
577 // rotated to portrait format.
579 int a = (int)ConvertAnyFormat(ValuePtr, Format);
580 if (m_ExifImageWidth < a) m_ExifImageWidth = a;
582 break;
584 case TAG_FOCALPLANEUNITS:
585 switch((int)ConvertAnyFormat(ValuePtr, Format))
587 // According to the information I was using, 2 means meters.
588 // But looking at the Cannon powershot's files, inches is the only
589 // sensible value.
590 case 1: m_FocalPlaneUnits = 25.4; break; // inch
591 case 2: m_FocalPlaneUnits = 25.4; break;
592 case 3: m_FocalPlaneUnits = 10; break; // centimeter
593 case 4: m_FocalPlaneUnits = 1; break; // millimeter
594 case 5: m_FocalPlaneUnits = .001; break; // micrometer
596 break;
598 case TAG_EXPOSURE_BIAS:
599 m_ExifInfo->ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
600 break;
602 case TAG_WHITEBALANCE:
603 m_ExifInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
604 break;
606 case TAG_LIGHT_SOURCE:
607 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
608 m_ExifInfo->LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
609 break;
611 case TAG_METERING_MODE:
612 m_ExifInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
613 break;
615 case TAG_EXPOSURE_PROGRAM:
616 m_ExifInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
617 break;
619 case TAG_EXPOSURE_INDEX:
620 if (m_ExifInfo->ISOequivalent == 0)
622 // Exposure index and ISO equivalent are often used interchangeably,
623 // so we will do the same.
624 // http://photography.about.com/library/glossary/bldef_ei.htm
625 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
627 break;
629 case TAG_ISO_EQUIVALENT:
630 m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
631 if (m_ExifInfo->ISOequivalent < 50)
632 m_ExifInfo->ISOequivalent *= 200; // Fixes strange encoding on some older digicams.
633 break;
635 case TAG_EXPOSURE_MODE:
636 m_ExifInfo->ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
637 break;
639 case TAG_DIGITALZOOMRATIO:
640 m_ExifInfo->DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
641 break;
643 case TAG_EXIF_OFFSET:
644 case TAG_INTEROP_OFFSET:
646 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
647 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
649 ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
651 else
653 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
655 continue;
657 break;
659 case TAG_GPSINFO:
661 const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
662 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
664 ErrNonfatal("Illegal GPS directory link",0,0);
666 else
668 ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
670 continue;
672 break;
674 case TAG_FOCALLENGTH_35MM:
675 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
676 // if its present, use it to compute equivalent focal length instead of
677 // computing it from sensor geometry and actual focal length.
678 m_ExifInfo->FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
679 break;
684 // In addition to linking to subdirectories via exif tags,
685 // there's also a potential link to another directory at the end of each
686 // directory. this has got to be the result of a committee!
687 unsigned Offset;
689 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength)
691 Offset = (unsigned)Get32(DirStart+2+12*NumDirEntries, m_MotorolaOrder);
692 if (Offset)
694 const unsigned char* const SubdirStart = OffsetBase + Offset;
695 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase)
697 if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20)
699 // Jhead 1.3 or earlier would crop the whole directory!
700 // As Jhead produces this form of format incorrectness,
701 // I'll just let it pass silently
703 else
705 ErrNonfatal("Illegal subdirectory link",0,0);
708 else
710 if (SubdirStart <= OffsetBase+ExifLength)
712 ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
715 if (Offset > m_LargestExifOffset)
717 m_LargestExifOffset = Offset;
721 else
723 // The exif header ends before the last next directory pointer.
726 if (m_ExifInfo->ThumbnailOffset)
728 m_ExifInfo->ThumbnailAtEnd = false;
730 if (m_ExifInfo->ThumbnailOffset <= ExifLength)
732 if (m_ExifInfo->ThumbnailSize > ExifLength - m_ExifInfo->ThumbnailOffset)
734 // If thumbnail extends past exif header, only save the part that
735 // actually exists. Canon's EOS viewer utility will do this - the
736 // thumbnail extracts ok with this hack.
737 m_ExifInfo->ThumbnailSize = ExifLength - m_ExifInfo->ThumbnailOffset;
744 //--------------------------------------------------------------------------
745 // Process a EXIF marker
746 // Describes all the drivel that most digital cameras include...
747 //--------------------------------------------------------------------------
748 bool CExifParse::Process (const unsigned char* const ExifSection, const unsigned short length, ExifInfo_t *info)
750 m_ExifInfo = info;
751 // EXIF signature: "Exif\0\0"
752 // Check EXIF signatures
753 const char ExifHeader[] = "Exif\0\0";
754 const char ExifAlignment0[] = "II";
755 const char ExifAlignment1[] = "MM";
756 const char ExifExtra = 0x2a;
758 const char* pos = (const char*)(ExifSection + sizeof(short)); // position data pointer after length field
760 if (memcmp(pos, ExifHeader,6))
762 printf("ExifParse: incorrect Exif header");
763 return false;
765 pos += 6;
767 if (memcmp(pos, ExifAlignment0, strlen(ExifAlignment0)) == 0)
769 m_MotorolaOrder = false;
771 else if (memcmp(pos, ExifAlignment1, strlen(ExifAlignment1)) == 0)
773 m_MotorolaOrder = true;
775 else
777 printf("ExifParse: invalid Exif alignment marker");
778 return false;
780 pos += strlen(ExifAlignment0);
782 // Check the next value for correctness.
783 if (Get16((const void*)(pos), m_MotorolaOrder) != ExifExtra)
785 printf("ExifParse: invalid Exif start (1)");
786 return false;
788 pos += sizeof(short);
790 unsigned long FirstOffset = (unsigned)Get32((const void*)pos, m_MotorolaOrder);
791 if (FirstOffset < 8 || FirstOffset + 8 >= length)
793 ErrNonfatal("Invalid offset of first IFD value: %u", FirstOffset, 0);
794 return false;
799 // First directory starts 16 bytes in. All offset are relative to 8 bytes in.
800 ProcessDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
802 m_ExifInfo->ThumbnailAtEnd = m_ExifInfo->ThumbnailOffset >= m_LargestExifOffset;
804 // Compute the CCD width, in millimeters.
805 if (m_FocalPlaneXRes != 0)
807 // Note: With some cameras, its not possible to compute this correctly because
808 // they don't adjust the indicated focal plane resolution units when using less
809 // than maximum resolution, so the CCDWidth value comes out too small. Nothing
810 // that Jhead can do about it - its a camera problem.
811 m_ExifInfo->CCDWidth = (float)(m_ExifImageWidth * m_FocalPlaneUnits / m_FocalPlaneXRes);
814 if (m_ExifInfo->FocalLength)
816 if (m_ExifInfo->FocalLength35mmEquiv == 0)
818 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
819 // already got it explicitly from a tag.
820 if (m_ExifInfo->CCDWidth != 0.0f)
822 m_ExifInfo->FocalLength35mmEquiv =
823 (int)(m_ExifInfo->FocalLength / m_ExifInfo->CCDWidth * 36 + 0.5f);
827 return true;
832 //--------------------------------------------------------------------------
833 // GPS Lat/Long extraction helper function
834 //--------------------------------------------------------------------------
835 void CExifParse::GetLatLong(
836 const unsigned int Format,
837 const unsigned char* ValuePtr,
838 const int ComponentSize,
839 char *latLongString)
841 if (Format != FMT_URATIONAL)
843 ErrNonfatal("Illegal number format %d for GPS Lat/Long", Format, 0);
845 else
847 double Values[3];
848 for (unsigned a=0; a<3 ;a++)
850 Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
852 if (Values[0] < 0 || Values[0] > 180 || Values[1] < 0 || Values[1] >= 60 || Values[2] < 0 || Values[2] >= 60)
854 // Ignore invalid values (DMS format expected)
855 ErrNonfatal("Invalid Lat/Long value", 0, 0);
856 latLongString[0] = 0;
858 else
860 char latLong[30];
861 sprintf(latLong, "%3.0fd %2.0f' %5.2f\"", Values[0], Values[1], Values[2]);
862 strcat(latLongString, latLong);
867 //--------------------------------------------------------------------------
868 // Process GPS info directory
869 //--------------------------------------------------------------------------
870 void CExifParse::ProcessGpsInfo(
871 const unsigned char* const DirStart,
872 int ByteCountUnused,
873 const unsigned char* const OffsetBase,
874 unsigned ExifLength)
876 int NumDirEntries = Get16(DirStart, m_MotorolaOrder);
878 for (int de=0;de<NumDirEntries;de++)
880 const unsigned char* DirEntry = DIR_ENTRY_ADDR(DirStart, de);
882 // Fix from aosp 34a2564d3268a5ca1472c5076675782fbaf724d6
883 if (DirEntry + 12 > OffsetBase + ExifLength)
885 ErrNonfatal("GPS info directory goes past end of exif", 0, 0);
886 return;
889 unsigned Tag = Get16(DirEntry, m_MotorolaOrder);
890 unsigned Format = Get16(DirEntry+2, m_MotorolaOrder);
891 unsigned Components = (unsigned)Get32(DirEntry+4, m_MotorolaOrder);
892 if (Format == 0 || Format > NUM_FORMATS)
894 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
895 continue;
898 unsigned ComponentSize = BytesPerFormat[Format - 1];
899 unsigned ByteCount = Components * ComponentSize;
901 const unsigned char* ValuePtr;
903 if (ByteCount > 4)
905 unsigned OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
906 // If its bigger than 4 bytes, the dir entry contains an offset.
907 if (OffsetVal > UINT32_MAX - ByteCount || OffsetVal + ByteCount > ExifLength)
909 // Bogus pointer offset and / or bytecount value
910 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
911 continue;
913 ValuePtr = OffsetBase+OffsetVal;
915 else
917 // 4 bytes or less and value is in the dir entry itself
918 ValuePtr = DirEntry+8;
921 switch(Tag)
923 case TAG_GPS_LAT_REF:
924 m_ExifInfo->GpsLat[0] = ValuePtr[0];
925 m_ExifInfo->GpsLat[1] = 0;
926 break;
928 case TAG_GPS_LONG_REF:
929 m_ExifInfo->GpsLong[0] = ValuePtr[0];
930 m_ExifInfo->GpsLong[1] = 0;
931 break;
933 case TAG_GPS_LAT:
934 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLat);
935 break;
936 case TAG_GPS_LONG:
937 GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLong);
938 break;
940 case TAG_GPS_ALT_REF:
941 if (ValuePtr[0] != 0)
942 m_ExifInfo->GpsAlt[0] = '-';
943 m_ExifInfo->GpsAlt[1] = 0;
944 break;
946 case TAG_GPS_ALT:
948 char temp[18];
949 sprintf(temp, "%.2fm", static_cast<double>(ConvertAnyFormat(ValuePtr, Format)));
950 strcat(m_ExifInfo->GpsAlt, temp);
952 break;