/*****************************************************************************/
// Copyright 2006-2008 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exif.cpp#1 $ */
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_exif.h"
#include "dng_tag_codes.h"
#include "dng_tag_types.h"
#include "dng_parse_utils.h"
#include "dng_globals.h"
#include "dng_exceptions.h"
#include "dng_tag_values.h"
#include "dng_utils.h"
/*****************************************************************************/
dng_exif::dng_exif ()
: fImageDescription ()
, fMake ()
, fModel ()
, fSoftware ()
, fArtist ()
, fCopyright ()
, fCopyright2 ()
, fUserComment ()
, fDateTime ()
, fDateTimeStorageInfo ()
, fDateTimeOriginal ()
, fDateTimeOriginalStorageInfo ()
, fDateTimeDigitized ()
, fDateTimeDigitizedStorageInfo ()
, fTIFF_EP_StandardID (0)
, fExifVersion (0)
, fFlashPixVersion (0)
, fExposureTime ()
, fFNumber ()
, fShutterSpeedValue ()
, fApertureValue ()
, fBrightnessValue ()
, fExposureBiasValue ()
, fMaxApertureValue ()
, fFocalLength ()
, fDigitalZoomRatio ()
, fExposureIndex ()
, fSubjectDistance ()
, fGamma ()
, fBatteryLevelR ()
, fBatteryLevelA ()
, fExposureProgram (0xFFFFFFFF)
, fMeteringMode (0xFFFFFFFF)
, fLightSource (0xFFFFFFFF)
, fFlash (0xFFFFFFFF)
, fFlashMask (0x0000FFFF)
, fSensingMethod (0xFFFFFFFF)
, fColorSpace (0xFFFFFFFF)
, fFileSource (0xFFFFFFFF)
, fSceneType (0xFFFFFFFF)
, fCustomRendered (0xFFFFFFFF)
, fExposureMode (0xFFFFFFFF)
, fWhiteBalance (0xFFFFFFFF)
, fSceneCaptureType (0xFFFFFFFF)
, fGainControl (0xFFFFFFFF)
, fContrast (0xFFFFFFFF)
, fSaturation (0xFFFFFFFF)
, fSharpness (0xFFFFFFFF)
, fSubjectDistanceRange (0xFFFFFFFF)
, fSelfTimerMode (0xFFFFFFFF)
, fImageNumber (0xFFFFFFFF)
, fFocalLengthIn35mmFilm (0)
, fSensitivityType (0)
, fStandardOutputSensitivity (0)
, fRecommendedExposureIndex (0)
, fISOSpeed (0)
, fISOSpeedLatitudeyyy (0)
, fISOSpeedLatitudezzz (0)
, fSubjectAreaCount (0)
, fComponentsConfiguration (0)
, fCompresssedBitsPerPixel ()
, fPixelXDimension (0)
, fPixelYDimension (0)
, fFocalPlaneXResolution ()
, fFocalPlaneYResolution ()
, fFocalPlaneResolutionUnit (0xFFFFFFFF)
, fCFARepeatPatternRows (0)
, fCFARepeatPatternCols (0)
, fImageUniqueID ()
, fGPSVersionID (0)
, fGPSLatitudeRef ()
, fGPSLongitudeRef ()
, fGPSAltitudeRef (0xFFFFFFFF)
, fGPSAltitude ()
, fGPSSatellites ()
, fGPSStatus ()
, fGPSMeasureMode ()
, fGPSDOP ()
, fGPSSpeedRef ()
, fGPSSpeed ()
, fGPSTrackRef ()
, fGPSTrack ()
, fGPSImgDirectionRef ()
, fGPSImgDirection ()
, fGPSMapDatum ()
, fGPSDestLatitudeRef ()
, fGPSDestLongitudeRef ()
, fGPSDestBearingRef ()
, fGPSDestBearing ()
, fGPSDestDistanceRef ()
, fGPSDestDistance ()
, fGPSProcessingMethod ()
, fGPSAreaInformation ()
, fGPSDateStamp ()
, fGPSDifferential (0xFFFFFFFF)
, fGPSHPositioningError ()
, fInteroperabilityIndex ()
, fInteroperabilityVersion (0)
, fRelatedImageFileFormat ()
, fRelatedImageWidth (0)
, fRelatedImageLength (0)
, fCameraSerialNumber ()
, fLensID ()
, fLensMake ()
, fLensName ()
, fLensSerialNumber ()
, fLensNameWasReadFromExif (false)
, fApproxFocusDistance ()
, fFlashCompensation ()
, fOwnerName ()
, fFirmware ()
{
uint32 j;
uint32 k;
fISOSpeedRatings [0] = 0;
fISOSpeedRatings [1] = 0;
fISOSpeedRatings [2] = 0;
for (j = 0; j < kMaxCFAPattern; j++)
for (k = 0; k < kMaxCFAPattern; k++)
{
fCFAPattern [j] [k] = 255;
}
}
/*****************************************************************************/
dng_exif::~dng_exif ()
{
}
/*****************************************************************************/
dng_exif * dng_exif::Clone () const
{
dng_exif *result = new dng_exif (*this);
if (!result)
{
ThrowMemoryFull ();
}
return result;
}
/*****************************************************************************/
void dng_exif::SetEmpty ()
{
*this = dng_exif ();
}
/*****************************************************************************/
void dng_exif::CopyGPSFrom (const dng_exif &exif)
{
fGPSVersionID = exif.fGPSVersionID;
fGPSLatitudeRef = exif.fGPSLatitudeRef;
fGPSLatitude [0] = exif.fGPSLatitude [0];
fGPSLatitude [1] = exif.fGPSLatitude [1];
fGPSLatitude [2] = exif.fGPSLatitude [2];
fGPSLongitudeRef = exif.fGPSLongitudeRef;
fGPSLongitude [0] = exif.fGPSLongitude [0];
fGPSLongitude [1] = exif.fGPSLongitude [1];
fGPSLongitude [2] = exif.fGPSLongitude [2];
fGPSAltitudeRef = exif.fGPSAltitudeRef;
fGPSAltitude = exif.fGPSAltitude;
fGPSTimeStamp [0] = exif.fGPSTimeStamp [0];
fGPSTimeStamp [1] = exif.fGPSTimeStamp [1];
fGPSTimeStamp [2] = exif.fGPSTimeStamp [2];
fGPSSatellites = exif.fGPSSatellites;
fGPSStatus = exif.fGPSStatus;
fGPSMeasureMode = exif.fGPSMeasureMode;
fGPSDOP = exif.fGPSDOP;
fGPSSpeedRef = exif.fGPSSpeedRef;
fGPSSpeed = exif.fGPSSpeed;
fGPSTrackRef = exif.fGPSTrackRef;
fGPSTrack = exif.fGPSTrack;
fGPSImgDirectionRef = exif.fGPSImgDirectionRef;
fGPSImgDirection = exif.fGPSImgDirection;
fGPSMapDatum = exif.fGPSMapDatum;
fGPSDestLatitudeRef = exif.fGPSDestLatitudeRef;
fGPSDestLatitude [0] = exif.fGPSDestLatitude [0];
fGPSDestLatitude [1] = exif.fGPSDestLatitude [1];
fGPSDestLatitude [2] = exif.fGPSDestLatitude [2];
fGPSDestLongitudeRef = exif.fGPSDestLongitudeRef;
fGPSDestLongitude [0] = exif.fGPSDestLongitude [0];
fGPSDestLongitude [1] = exif.fGPSDestLongitude [1];
fGPSDestLongitude [2] = exif.fGPSDestLongitude [2];
fGPSDestBearingRef = exif.fGPSDestBearingRef;
fGPSDestBearing = exif.fGPSDestBearing;
fGPSDestDistanceRef = exif.fGPSDestDistanceRef;
fGPSDestDistance = exif.fGPSDestDistance;
fGPSProcessingMethod = exif.fGPSProcessingMethod;
fGPSAreaInformation = exif.fGPSAreaInformation;
fGPSDateStamp = exif.fGPSDateStamp;
fGPSDifferential = exif.fGPSDifferential;
fGPSHPositioningError = exif.fGPSHPositioningError;
}
/*****************************************************************************/
// Fix up common errors and rounding issues with EXIF exposure times.
real64 dng_exif::SnapExposureTime (real64 et)
{
// Protection against invalid values.
if (et <= 0.0)
return 0.0;
// If near a standard shutter speed, snap to it.
static const real64 kStandardSpeed [] =
{
30.0,
25.0,
20.0,
15.0,
13.0,
10.0,
8.0,
6.0,
5.0,
4.0,
3.2,
3.0,
2.5,
2.0,
1.6,
1.5,
1.3,
1.0,
0.8,
0.7,
0.6,
0.5,
0.4,
0.3,
1.0 / 4.0,
1.0 / 5.0,
1.0 / 6.0,
1.0 / 8.0,
1.0 / 10.0,
1.0 / 13.0,
1.0 / 15.0,
1.0 / 20.0,
1.0 / 25.0,
1.0 / 30.0,
1.0 / 40.0,
1.0 / 45.0,
1.0 / 50.0,
1.0 / 60.0,
1.0 / 80.0,
1.0 / 90.0,
1.0 / 100.0,
1.0 / 125.0,
1.0 / 160.0,
1.0 / 180.0,
1.0 / 200.0,
1.0 / 250.0,
1.0 / 320.0,
1.0 / 350.0,
1.0 / 400.0,
1.0 / 500.0,
1.0 / 640.0,
1.0 / 750.0,
1.0 / 800.0,
1.0 / 1000.0,
1.0 / 1250.0,
1.0 / 1500.0,
1.0 / 1600.0,
1.0 / 2000.0,
1.0 / 2500.0,
1.0 / 3000.0,
1.0 / 3200.0,
1.0 / 4000.0,
1.0 / 5000.0,
1.0 / 6000.0,
1.0 / 6400.0,
1.0 / 8000.0,
1.0 / 10000.0,
1.0 / 12000.0,
1.0 / 12800.0,
1.0 / 16000.0
};
uint32 count = sizeof (kStandardSpeed ) /
sizeof (kStandardSpeed [0]);
for (uint32 fudge = 0; fudge <= 1; fudge++)
{
real64 testSpeed = et;
if (fudge == 1)
{
// Often APEX values are rounded to a power of two,
// which results in non-standard shutter speeds.
if (et >= 0.1)
{
// No fudging slower than 1/10 second
break;
}
else if (et >= 0.01)
{
// Between 1/10 and 1/100 the commonly misrounded
// speeds are 1/15, 1/30, 1/60, which are often encoded as
// 1/16, 1/32, 1/64. Try fudging and see if we get
// near a standard speed.
testSpeed *= 16.0 / 15.0;
}
else
{
// Faster than 1/100, the commonly misrounded
// speeds are 1/125, 1/250, 1/500, etc., which
// are often encoded as 1/128, 1/256, 1/512.
testSpeed *= 128.0 / 125.0;
}
}
for (uint32 index = 0; index < count; index++)
{
if (testSpeed >= kStandardSpeed [index] * 0.98 &&
testSpeed <= kStandardSpeed [index] * 1.02)
{
return kStandardSpeed [index];
}
}
}
// We are not near any standard speeds. Round the non-standard value to something
// that looks reasonable.
if (et >= 10.0)
{
// Round to nearest second.
et = floor (et + 0.5);
}
else if (et >= 0.5)
{
// Round to nearest 1/10 second
et = floor (et * 10.0 + 0.5) * 0.1;
}
else if (et >= 1.0 / 20.0)
{
// Round to an exact inverse.
et = 1.0 / floor (1.0 / et + 0.5);
}
else if (et >= 1.0 / 130.0)
{
// Round inverse to multiple of 5
et = 0.2 / floor (0.2 / et + 0.5);
}
else if (et >= 1.0 / 750.0)
{
// Round inverse to multiple of 10
et = 0.1 / floor (0.1 / et + 0.5);
}
else if (et >= 1.0 / 1300.0)
{
// Round inverse to multiple of 50
et = 0.02 / floor (0.02 / et + 0.5);
}
else if (et >= 1.0 / 15000.0)
{
// Round inverse to multiple of 100
et = 0.01 / floor (0.01 / et + 0.5);
}
else
{
// Round inverse to multiple of 1000
et = 0.001 / floor (0.001 / et + 0.5);
}
return et;
}
/*****************************************************************************/
void dng_exif::SetExposureTime (real64 et, bool snap)
{
fExposureTime.Clear ();
fShutterSpeedValue.Clear ();
if (snap)
{
et = SnapExposureTime (et);
}
if (et >= 1.0 / 32768.0 && et <= 32768.0)
{
if (et >= 100.0)
{
fExposureTime.Set_real64 (et, 1);
}
else if (et >= 1.0)
{
fExposureTime.Set_real64 (et, 10);
fExposureTime.ReduceByFactor (10);
}
else if (et <= 0.1)
{
fExposureTime = dng_urational (1, Round_uint32 (1.0 / et));
}
else
{
fExposureTime.Set_real64 (et, 100);
fExposureTime.ReduceByFactor (10);
for (uint32 f = 2; f <= 9; f++)
{
real64 z = 1.0 / (real64) f / et;
if (z >= 0.99 && z <= 1.01)
{
fExposureTime = dng_urational (1, f);
break;
}
}
}
// Now mirror this value to the ShutterSpeedValue field.
et = fExposureTime.As_real64 ();
fShutterSpeedValue.Set_real64 (-log (et) / log (2.0), 1000000);
fShutterSpeedValue.ReduceByFactor (10);
fShutterSpeedValue.ReduceByFactor (10);
fShutterSpeedValue.ReduceByFactor (10);
fShutterSpeedValue.ReduceByFactor (10);
fShutterSpeedValue.ReduceByFactor (10);
fShutterSpeedValue.ReduceByFactor (10);
}
}
/*****************************************************************************/
void dng_exif::SetShutterSpeedValue (real64 ss)
{
if (fExposureTime.NotValid ())
{
real64 et = pow (2.0, -ss);
SetExposureTime (et, true);
}
}
/******************************************************************************/
dng_urational dng_exif::EncodeFNumber (real64 fs)
{
dng_urational y;
if (fs > 10.0)
{
y.Set_real64 (fs, 1);
}
else if (fs < 1.0)
{
y.Set_real64 (fs, 100);
y.ReduceByFactor (10);
y.ReduceByFactor (10);
}
else
{
y.Set_real64 (fs, 10);
y.ReduceByFactor (10);
}
return y;
}
/*****************************************************************************/
void dng_exif::SetFNumber (real64 fs)
{
fFNumber.Clear ();
fApertureValue.Clear ();
// Allow f-number values less than 1.0 (e.g., f/0.95), even though they would
// correspond to negative APEX values, which the EXIF specification does not
// support (ApertureValue is a rational, not srational). The ApertureValue tag
// will be omitted in the case where fs < 1.0.
if (fs > 0.0 && fs <= 32768.0)
{
fFNumber = EncodeFNumber (fs);
// Now mirror this value to the ApertureValue field.
real64 av = FNumberToApertureValue (fFNumber);
if (av >= 0.0 && av <= 99.99)
{
fApertureValue.Set_real64 (av, 1000000);
fApertureValue.ReduceByFactor (10);
fApertureValue.ReduceByFactor (10);
fApertureValue.ReduceByFactor (10);
fApertureValue.ReduceByFactor (10);
fApertureValue.ReduceByFactor (10);
fApertureValue.ReduceByFactor (10);
}
}
}
/*****************************************************************************/
void dng_exif::SetApertureValue (real64 av)
{
if (fFNumber.NotValid ())
{
SetFNumber (ApertureValueToFNumber (av));
}
}
/*****************************************************************************/
real64 dng_exif::ApertureValueToFNumber (real64 av)
{
return pow (2.0, 0.5 * av);
}
/*****************************************************************************/
real64 dng_exif::ApertureValueToFNumber (const dng_urational &av)
{
return ApertureValueToFNumber (av.As_real64 ());
}
/*****************************************************************************/
real64 dng_exif::FNumberToApertureValue (real64 fNumber)
{
return 2.0 * log (fNumber) / log (2.0);
}
/*****************************************************************************/
real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber)
{
return FNumberToApertureValue (fNumber.As_real64 ());
}
/*****************************************************************************/
void dng_exif::UpdateDateTime (const dng_date_time_info &dt)
{
fDateTime = dt;
}
/*****************************************************************************/
bool dng_exif::AtLeastVersion0230 () const
{
uint32 b0 = (fExifVersion >> 24) & 0xff;
uint32 b1 = (fExifVersion >> 16) & 0xff;
uint32 b2 = (fExifVersion >> 8) & 0xff;
return (b0 > 0) || (b1 >= 2 && b2 >= 3);
}
/*****************************************************************************/
bool dng_exif::ParseTag (dng_stream &stream,
dng_shared &shared,
uint32 parentCode,
bool isMainIFD,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 tagOffset)
{
if (parentCode == 0)
{
if (Parse_ifd0 (stream,
shared,
parentCode,
tagCode,
tagType,
tagCount,
tagOffset))
{
return true;
}
}
if (parentCode == 0 || isMainIFD)
{
if (Parse_ifd0_main (stream,
shared,
parentCode,
tagCode,
tagType,
tagCount,
tagOffset))
{
return true;
}
}
if (parentCode == 0 ||
parentCode == tcExifIFD)
{
if (Parse_ifd0_exif (stream,
shared,
parentCode,
tagCode,
tagType,
tagCount,
tagOffset))
{
return true;
}
}
if (parentCode == tcGPSInfo)
{
if (Parse_gps (stream,
shared,
parentCode,
tagCode,
tagType,
tagCount,
tagOffset))
{
return true;
}
}
if (parentCode == tcInteroperabilityIFD)
{
if (Parse_interoperability (stream,
shared,
parentCode,
tagCode,
tagType,
tagCount,
tagOffset))
{
return true;
}
}
return false;
}
/*****************************************************************************/
// Parses tags that should only appear in IFD 0.
bool dng_exif::Parse_ifd0 (dng_stream &stream,
dng_shared & /* shared */,
uint32 parentCode,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 /* tagOffset */)
{
switch (tagCode)
{
case tcImageDescription:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fImageDescription);
#if qDNGValidate
if (gVerbose)
{
printf ("ImageDescription: ");
DumpString (fImageDescription);
printf ("\n");
}
#endif
break;
}
case tcMake:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fMake);
#if qDNGValidate
if (gVerbose)
{
printf ("Make: ");
DumpString (fMake);
printf ("\n");
}
#endif
break;
}
case tcModel:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fModel);
#if qDNGValidate
if (gVerbose)
{
printf ("Model: ");
DumpString (fModel);
printf ("\n");
}
#endif
break;
}
case tcSoftware:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fSoftware);
#if qDNGValidate
if (gVerbose)
{
printf ("Software: ");
DumpString (fSoftware);
printf ("\n");
}
#endif
break;
}
case tcDateTime:
{
uint64 tagPosition = stream.PositionInOriginalFile ();
dng_date_time dt;
if (!ParseDateTimeTag (stream,
parentCode,
tagCode,
tagType,
tagCount,
dt))
{
return false;
}
fDateTime.SetDateTime (dt);
fDateTimeStorageInfo = dng_date_time_storage_info (tagPosition,
dng_date_time_format_exif);
#if qDNGValidate
if (gVerbose)
{
printf ("DateTime: ");
DumpDateTime (fDateTime.DateTime ());
printf ("\n");
}
#endif
break;
}
case tcArtist:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fArtist);
#if qDNGValidate
if (gVerbose)
{
printf ("Artist: ");
DumpString (fArtist);
printf ("\n");
}
#endif
break;
}
case tcCopyright:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseDualStringTag (stream,
parentCode,
tagCode,
tagCount,
fCopyright,
fCopyright2);
#if qDNGValidate
if (gVerbose)
{
printf ("Copyright: ");
DumpString (fCopyright);
if (fCopyright2.Get () [0] != 0)
{
printf (" ");
DumpString (fCopyright2);
}
printf ("\n");
}
#endif
break;
}
case tcTIFF_EP_StandardID:
{
CheckTagType (parentCode, tagCode, tagType, ttByte);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fTIFF_EP_StandardID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
printf ("TIFF/EPStandardID: %u.%u.%u.%u\n",
(unsigned) b0,
(unsigned) b1,
(unsigned) b2,
(unsigned) b3);
}
#endif
break;
}
case tcCameraSerialNumber:
case tcKodakCameraSerialNumber: // Kodak uses a very similar tag.
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fCameraSerialNumber);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (fCameraSerialNumber);
printf ("\n");
}
#endif
break;
}
case tcLensInfo:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
return false;
fLensInfo [0] = stream.TagValue_urational (tagType);
fLensInfo [1] = stream.TagValue_urational (tagType);
fLensInfo [2] = stream.TagValue_urational (tagType);
fLensInfo [3] = stream.TagValue_urational (tagType);
// Some third party software wrote zero rather and undefined values
// for unknown entries. Work around this bug.
for (uint32 j = 0; j < 4; j++)
{
if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
{
fLensInfo [j] = dng_urational (0, 0);
#if qDNGValidate
ReportWarning ("Zero entry in LensInfo tag--should be undefined");
#endif
}
}
#if qDNGValidate
if (gVerbose)
{
printf ("LensInfo: ");
real64 minFL = fLensInfo [0].As_real64 ();
real64 maxFL = fLensInfo [1].As_real64 ();
if (minFL == maxFL)
printf ("%0.1f mm", minFL);
else
printf ("%0.1f-%0.1f mm", minFL, maxFL);
if (fLensInfo [2].d)
{
real64 minFS = fLensInfo [2].As_real64 ();
real64 maxFS = fLensInfo [3].As_real64 ();
if (minFS == maxFS)
printf (" f/%0.1f", minFS);
else
printf (" f/%0.1f-%0.1f", minFS, maxFS);
}
printf ("\n");
}
#endif
break;
}
default:
{
return false;
}
}
return true;
}
/*****************************************************************************/
// Parses tags that should only appear in IFD 0 or the main image IFD.
bool dng_exif::Parse_ifd0_main (dng_stream &stream,
dng_shared & /* shared */,
uint32 parentCode,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 /* tagOffset */)
{
switch (tagCode)
{
case tcFocalPlaneXResolution:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneXResolution = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneXResolution: %0.4f\n",
fFocalPlaneXResolution.As_real64 ());
}
#endif
break;
}
case tcFocalPlaneYResolution:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneYResolution = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneYResolution: %0.4f\n",
fFocalPlaneYResolution.As_real64 ());
}
#endif
break;
}
case tcFocalPlaneResolutionUnit:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneResolutionUnit: %s\n",
LookupResolutionUnit (fFocalPlaneResolutionUnit));
}
#endif
break;
}
case tcSensingMethod:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSensingMethod = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("SensingMethod: %s\n",
LookupSensingMethod (fSensingMethod));
}
#endif
break;
}
default:
{
return false;
}
}
return true;
}
/*****************************************************************************/
// Parses tags that should only appear in IFD 0 or EXIF IFD.
bool dng_exif::Parse_ifd0_exif (dng_stream &stream,
dng_shared & /* shared */,
uint32 parentCode,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 /* tagOffset */)
{
switch (tagCode)
{
case tcBatteryLevel:
{
CheckTagType (parentCode, tagCode, tagType, ttRational, ttAscii);
if (tagType == ttAscii)
{
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fBatteryLevelA);
}
else
{
CheckTagCount (parentCode, tagCode, tagCount, 1);
fBatteryLevelR = stream.TagValue_urational (tagType);
}
#if qDNGValidate
if (gVerbose)
{
printf ("BatteryLevel: ");
if (tagType == ttAscii)
{
DumpString (fBatteryLevelA);
}
else
{
printf ("%0.2f", fBatteryLevelR.As_real64 ());
}
printf ("\n");
}
#endif
break;
}
case tcExposureTime:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
dng_urational et = stream.TagValue_urational (tagType);
SetExposureTime (et.As_real64 (), true);
#if qDNGValidate
if (gVerbose)
{
printf ("ExposureTime: ");
DumpExposureTime (et.As_real64 ());
printf ("\n");
}
if (et.As_real64 () <= 0.0)
{
ReportWarning ("The ExposureTime is <= 0");
}
#endif
break;
}
case tcFNumber:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
dng_urational fs = stream.TagValue_urational (tagType);
// Sometimes "unknown" is recorded as zero.
if (fs.As_real64 () <= 0.0)
{
fs.Clear ();
}
#if qDNGValidate
if (gVerbose)
{
printf ("FNumber: f/%0.2f\n",
fs.As_real64 ());
}
#endif
SetFNumber (fs.As_real64 ());
break;
}
case tcExposureProgram:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fExposureProgram = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ExposureProgram: %s\n",
LookupExposureProgram (fExposureProgram));
}
#endif
break;
}
case tcISOSpeedRatings:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1, 3);
for (uint32 j = 0; j < tagCount && j < 3; j++)
{
fISOSpeedRatings [j] = stream.TagValue_uint32 (tagType);
}
#if qDNGValidate
if (gVerbose)
{
printf ("ISOSpeedRatings:");
for (uint32 j = 0; j < tagCount && j < 3; j++)
{
printf (" %u", (unsigned) fISOSpeedRatings [j]);
}
printf ("\n");
}
#endif
break;
}
case tcSensitivityType:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSensitivityType = (uint32) stream.Get_uint16 ();
#if qDNGValidate
if (gVerbose)
{
printf ("SensitivityType: %s\n",
LookupSensitivityType (fSensitivityType));
}
#endif
break;
}
case tcStandardOutputSensitivity:
{
CheckTagType (parentCode, tagCode, tagType, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fStandardOutputSensitivity = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("StandardOutputSensitivity: %u\n",
(unsigned) fStandardOutputSensitivity);
}
#endif
break;
}
case tcRecommendedExposureIndex:
{
CheckTagType (parentCode, tagCode, tagType, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fRecommendedExposureIndex = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("RecommendedExposureIndex: %u\n",
(unsigned) fRecommendedExposureIndex);
}
#endif
break;
}
case tcISOSpeed:
{
CheckTagType (parentCode, tagCode, tagType, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fISOSpeed = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ISOSpeed: %u\n",
(unsigned) fISOSpeed);
}
#endif
break;
}
case tcISOSpeedLatitudeyyy:
{
CheckTagType (parentCode, tagCode, tagType, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fISOSpeedLatitudeyyy = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ISOSpeedLatitudeyyy: %u\n",
(unsigned) fISOSpeedLatitudeyyy);
}
#endif
break;
}
case tcISOSpeedLatitudezzz:
{
CheckTagType (parentCode, tagCode, tagType, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fISOSpeedLatitudezzz = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ISOSpeedLatitudezzz: %u\n",
(unsigned) fISOSpeedLatitudezzz);
}
#endif
break;
}
case tcTimeZoneOffset:
{
CheckTagType (parentCode, tagCode, tagType, ttSShort);
CheckTagCount (parentCode, tagCode, tagCount, 1, 2);
dng_time_zone zoneOriginal;
zoneOriginal.SetOffsetHours (stream.TagValue_int32 (tagType));
fDateTimeOriginal.SetZone (zoneOriginal);
#if 0 // MWG: Not filling in time zones unless we are sure.
// Note that there is no "TimeZoneOffsetDigitized" field, so
// we assume the same tone zone as the original.
fDateTimeDigitized.SetZone (zoneOriginal);
#endif
dng_time_zone zoneCurrent;
if (tagCount >= 2)
{
zoneCurrent.SetOffsetHours (stream.TagValue_int32 (tagType));
fDateTime.SetZone (zoneCurrent);
}
#if qDNGValidate
if (gVerbose)
{
printf ("TimeZoneOffset: DateTimeOriginal = %d",
(int) zoneOriginal.ExactHourOffset ());
if (tagCount >= 2)
{
printf (", DateTime = %d",
(int) zoneCurrent.ExactHourOffset ());
}
printf ("\n");
}
#endif
break;
}
case tcSelfTimerMode:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSelfTimerMode = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("SelfTimerMode: ");
if (fSelfTimerMode)
{
printf ("%u sec", (unsigned) fSelfTimerMode);
}
else
{
printf ("Off");
}
printf ("\n");
}
#endif
break;
}
case tcExifVersion:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fExifVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
real64 x = (b0 - '0') * 10.00 +
(b1 - '0') * 1.00 +
(b2 - '0') * 0.10 +
(b3 - '0') * 0.01;
printf ("ExifVersion: %0.2f\n", x);
}
#endif
break;
}
case tcDateTimeOriginal:
{
uint64 tagPosition = stream.PositionInOriginalFile ();
dng_date_time dt;
if (!ParseDateTimeTag (stream,
parentCode,
tagCode,
tagType,
tagCount,
dt))
{
return false;
}
fDateTimeOriginal.SetDateTime (dt);
fDateTimeOriginalStorageInfo = dng_date_time_storage_info (tagPosition,
dng_date_time_format_exif);
#if qDNGValidate
if (gVerbose)
{
printf ("DateTimeOriginal: ");
DumpDateTime (fDateTimeOriginal.DateTime ());
printf ("\n");
}
#endif
break;
}
case tcDateTimeDigitized:
{
uint64 tagPosition = stream.PositionInOriginalFile ();
dng_date_time dt;
if (!ParseDateTimeTag (stream,
parentCode,
tagCode,
tagType,
tagCount,
dt))
{
return false;
}
fDateTimeDigitized.SetDateTime (dt);
fDateTimeDigitizedStorageInfo = dng_date_time_storage_info (tagPosition,
dng_date_time_format_exif);
#if qDNGValidate
if (gVerbose)
{
printf ("DateTimeDigitized: ");
DumpDateTime (fDateTimeDigitized.DateTime ());
printf ("\n");
}
#endif
break;
}
case tcComponentsConfiguration:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fComponentsConfiguration = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
printf ("ComponentsConfiguration: %s %s %s %s\n",
LookupComponent (b0),
LookupComponent (b1),
LookupComponent (b2),
LookupComponent (b3));
}
#endif
break;
}
case tcCompressedBitsPerPixel:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fCompresssedBitsPerPixel = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("CompressedBitsPerPixel: %0.2f\n",
fCompresssedBitsPerPixel.As_real64 ());
}
#endif
break;
}
case tcShutterSpeedValue:
{
CheckTagType (parentCode, tagCode, tagType, ttSRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
dng_srational ss = stream.TagValue_srational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ShutterSpeedValue: ");
real64 x = pow (2.0, -ss.As_real64 ());
DumpExposureTime (x);
printf ("\n");
}
// The ExposureTime and ShutterSpeedValue tags should be consistent.
if (fExposureTime.IsValid ())
{
real64 et = fExposureTime.As_real64 ();
real64 tv1 = -1.0 * log (et) / log (2.0);
real64 tv2 = ss.As_real64 ();
// Make sure they are within 0.25 APEX values.
if (Abs_real64 (tv1 - tv2) > 0.25)
{
ReportWarning ("The ExposureTime and ShutterSpeedValue tags have conflicting values");
}
}
#endif
SetShutterSpeedValue (ss.As_real64 ());
break;
}
case tcApertureValue:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
dng_urational av = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
real64 x = pow (2.0, 0.5 * av.As_real64 ());
printf ("ApertureValue: f/%0.2f\n", x);
}
// The FNumber and ApertureValue tags should be consistent.
if (fFNumber.IsValid () && av.IsValid ())
{
real64 fs = fFNumber.As_real64 ();
real64 av1 = FNumberToApertureValue (fs);
real64 av2 = av.As_real64 ();
if (Abs_real64 (av1 - av2) > 0.25)
{
ReportWarning ("The FNumber and ApertureValue tags have conflicting values");
}
}
#endif
SetApertureValue (av.As_real64 ());
break;
}
case tcBrightnessValue:
{
CheckTagType (parentCode, tagCode, tagType, ttSRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fBrightnessValue = stream.TagValue_srational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("BrightnessValue: %0.2f\n",
fBrightnessValue.As_real64 ());
}
#endif
break;
}
case tcExposureBiasValue:
{
CheckTagType (parentCode, tagCode, tagType, ttSRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fExposureBiasValue = stream.TagValue_srational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ExposureBiasValue: %0.2f\n",
fExposureBiasValue.As_real64 ());
}
#endif
break;
}
case tcMaxApertureValue:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fMaxApertureValue = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
real64 x = pow (2.0, 0.5 * fMaxApertureValue.As_real64 ());
printf ("MaxApertureValue: f/%0.1f\n", x);
}
#endif
break;
}
case tcSubjectDistance:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSubjectDistance = stream.TagValue_urational (tagType);
fApproxFocusDistance = fSubjectDistance;
#if qDNGValidate
if (gVerbose)
{
printf ("SubjectDistance: %u/%u\n",
(unsigned) fSubjectDistance.n,
(unsigned) fSubjectDistance.d);
}
#endif
break;
}
case tcMeteringMode:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fMeteringMode = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("MeteringMode: %s\n",
LookupMeteringMode (fMeteringMode));
}
#endif
break;
}
case tcLightSource:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fLightSource = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("LightSource: %s\n",
LookupLightSource (fLightSource));
}
#endif
break;
}
case tcFlash:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFlash = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("Flash: %u\n", (unsigned) fFlash);
if ((fFlash >> 5) & 1)
{
printf (" No flash function\n");
}
else
{
if (fFlash & 0x1)
{
printf (" Flash fired\n");
switch ((fFlash >> 1) & 0x3)
{
case 2:
printf (" Strobe return light not detected\n");
break;
case 3:
printf (" Strobe return light detected\n");
break;
}
}
else
{
printf (" Flash did not fire\n");
}
switch ((fFlash >> 3) & 0x3)
{
case 1:
printf (" Compulsory flash firing\n");
break;
case 2:
printf (" Compulsory flash suppression\n");
break;
case 3:
printf (" Auto mode\n");
break;
}
if ((fFlash >> 6) & 1)
{
printf (" Red-eye reduction supported\n");
}
}
}
#endif
break;
}
case tcFocalLength:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalLength = stream.TagValue_urational (tagType);
// Sometimes "unknown" is recorded as zero.
if (fFocalLength.As_real64 () <= 0.0)
{
fFocalLength.Clear ();
}
#if qDNGValidate
if (gVerbose)
{
printf ("FocalLength: %0.1f mm\n",
fFocalLength.As_real64 ());
}
#endif
break;
}
case tcImageNumber:
{
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fImageNumber = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ImageNumber: %u\n", (unsigned) fImageNumber);
}
#endif
break;
}
case tcExposureIndex:
case tcExposureIndexExif:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fExposureIndex = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ISO %0.1f\n",
LookupTagCode (parentCode, tagCode),
fExposureIndex.As_real64 ());
}
#endif
break;
}
case tcUserComment:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
ParseEncodedStringTag (stream,
parentCode,
tagCode,
tagCount,
fUserComment);
#if qDNGValidate
if (gVerbose)
{
printf ("UserComment: ");
DumpString (fUserComment);
printf ("\n");
}
#endif
break;
}
case tcSubsecTime:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
dng_string subsecs;
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
subsecs);
fDateTime.SetSubseconds (subsecs);
#if qDNGValidate
if (gVerbose)
{
printf ("SubsecTime: ");
DumpString (subsecs);
printf ("\n");
}
#endif
break;
}
case tcSubsecTimeOriginal:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
dng_string subsecs;
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
subsecs);
fDateTimeOriginal.SetSubseconds (subsecs);
#if qDNGValidate
if (gVerbose)
{
printf ("SubsecTimeOriginal: ");
DumpString (subsecs);
printf ("\n");
}
#endif
break;
}
case tcSubsecTimeDigitized:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
dng_string subsecs;
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
subsecs);
fDateTimeDigitized.SetSubseconds (subsecs);
#if qDNGValidate
if (gVerbose)
{
printf ("SubsecTimeDigitized: ");
DumpString (subsecs);
printf ("\n");
}
#endif
break;
}
case tcFlashPixVersion:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fFlashPixVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
real64 x = (b0 - '0') * 10.00 +
(b1 - '0') * 1.00 +
(b2 - '0') * 0.10 +
(b3 - '0') * 0.01;
printf ("FlashPixVersion: %0.2f\n", x);
}
#endif
break;
}
case tcColorSpace:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fColorSpace = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ColorSpace: %s\n",
LookupColorSpace (fColorSpace));
}
#endif
break;
}
case tcPixelXDimension:
{
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fPixelXDimension = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("PixelXDimension: %u\n", (unsigned) fPixelXDimension);
}
#endif
break;
}
case tcPixelYDimension:
{
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fPixelYDimension = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("PixelYDimension: %u\n", (unsigned) fPixelYDimension);
}
#endif
break;
}
case tcFocalPlaneXResolutionExif:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneXResolution = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneXResolutionExif: %0.4f\n",
fFocalPlaneXResolution.As_real64 ());
}
#endif
break;
}
case tcFocalPlaneYResolutionExif:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneYResolution = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneYResolutionExif: %0.4f\n",
fFocalPlaneYResolution.As_real64 ());
}
#endif
break;
}
case tcFocalPlaneResolutionUnitExif:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalPlaneResolutionUnitExif: %s\n",
LookupResolutionUnit (fFocalPlaneResolutionUnit));
}
#endif
break;
}
case tcSensingMethodExif:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSensingMethod = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("SensingMethodExif: %s\n",
LookupSensingMethod (fSensingMethod));
}
#endif
break;
}
case tcFileSource:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFileSource = stream.Get_uint8 ();
#if qDNGValidate
if (gVerbose)
{
printf ("FileSource: %s\n",
LookupFileSource (fFileSource));
}
#endif
break;
}
case tcSceneType:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSceneType = stream.Get_uint8 ();
#if qDNGValidate
if (gVerbose)
{
printf ("SceneType: %s\n",
LookupSceneType (fSceneType));
}
#endif
break;
}
case tcCFAPatternExif:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
if (tagCount <= 4)
{
return false;
}
uint32 cols = stream.Get_uint16 ();
uint32 rows = stream.Get_uint16 ();
if (tagCount != 4 + cols * rows)
{
return false;
}
if (cols < 1 || cols > kMaxCFAPattern ||
rows < 1 || rows > kMaxCFAPattern)
{
return false;
}
fCFARepeatPatternCols = cols;
fCFARepeatPatternRows = rows;
// Note that the Exif spec stores this array in a different
// scan order than the TIFF-EP spec.
for (uint32 j = 0; j < fCFARepeatPatternCols; j++)
for (uint32 k = 0; k < fCFARepeatPatternRows; k++)
{
fCFAPattern [k] [j] = stream.Get_uint8 ();
}
#if qDNGValidate
if (gVerbose)
{
printf ("CFAPatternExif:\n");
for (uint32 j = 0; j < fCFARepeatPatternRows; j++)
{
int32 spaces = 4;
for (uint32 k = 0; k < fCFARepeatPatternCols; k++)
{
while (spaces-- > 0)
{
printf (" ");
}
const char *name = LookupCFAColor (fCFAPattern [j] [k]);
spaces = 9 - (int32) strlen (name);
printf ("%s", name);
}
printf ("\n");
}
}
#endif
break;
}
case tcCustomRendered:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fCustomRendered = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("CustomRendered: %s\n",
LookupCustomRendered (fCustomRendered));
}
#endif
break;
}
case tcExposureMode:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fExposureMode = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("ExposureMode: %s\n",
LookupExposureMode (fExposureMode));
}
#endif
break;
}
case tcWhiteBalance:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fWhiteBalance = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("WhiteBalance: %s\n",
LookupWhiteBalance (fWhiteBalance));
}
#endif
break;
}
case tcDigitalZoomRatio:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fDigitalZoomRatio = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("DigitalZoomRatio: ");
if (fDigitalZoomRatio.n == 0 ||
fDigitalZoomRatio.d == 0)
{
printf ("Not used\n");
}
else
{
printf ("%0.2f\n", fDigitalZoomRatio.As_real64 ());
}
}
#endif
break;
}
case tcFocalLengthIn35mmFilm:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fFocalLengthIn35mmFilm = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("FocalLengthIn35mmFilm: %u mm\n",
(unsigned) fFocalLengthIn35mmFilm);
}
#endif
break;
}
case tcSceneCaptureType:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSceneCaptureType = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("SceneCaptureType: %s\n",
LookupSceneCaptureType (fSceneCaptureType));
}
#endif
break;
}
case tcGainControl:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fGainControl = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("GainControl: %s\n",
LookupGainControl (fGainControl));
}
#endif
break;
}
case tcContrast:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fContrast = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("Contrast: %s\n",
LookupContrast (fContrast));
}
#endif
break;
}
case tcSaturation:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSaturation = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("Saturation: %s\n",
LookupSaturation (fSaturation));
}
#endif
break;
}
case tcSharpness:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSharpness = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("Sharpness: %s\n",
LookupSharpness (fSharpness));
}
#endif
break;
}
case tcSubjectDistanceRange:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fSubjectDistanceRange = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("SubjectDistanceRange: %s\n",
LookupSubjectDistanceRange (fSubjectDistanceRange));
}
#endif
break;
}
case tcSubjectArea:
case tcSubjectLocation:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 4))
{
return false;
}
if (tagCode == tcSubjectLocation)
{
CheckTagCount (parentCode, tagCode, tagCount, 2);
}
fSubjectAreaCount = tagCount;
for (uint32 j = 0; j < tagCount; j++)
{
fSubjectArea [j] = stream.TagValue_uint32 (tagType);
}
#if qDNGValidate
if (gVerbose)
{
printf ("%s:", LookupTagCode (parentCode, tagCode));
for (uint32 j = 0; j < fSubjectAreaCount; j++)
{
printf (" %u", (unsigned) fSubjectArea [j]);
}
printf ("\n");
}
#endif
break;
}
case tcGamma:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fGamma = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("Gamma: %0.2f\n",
fGamma.As_real64 ());
}
#endif
break;
}
case tcImageUniqueID:
{
if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
return false;
if (!CheckTagCount (parentCode, tagCode, tagCount, 33))
return false;
dng_string s;
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
s);
if (s.Length () != 32)
return false;
dng_fingerprint f;
for (uint32 j = 0; j < 32; j++)
{
char c = ForceUppercase (s.Get () [j]);
uint32 digit;
if (c >= '0' && c <= '9')
{
digit = c - '0';
}
else if (c >= 'A' && c <= 'F')
{
digit = c - 'A' + 10;
}
else
return false;
f.data [j >> 1] *= 16;
f.data [j >> 1] += (uint8) digit;
}
fImageUniqueID = f;
#if qDNGValidate
if (gVerbose)
{
printf ("ImageUniqueID: ");
DumpFingerprint (fImageUniqueID);
printf ("\n");
}
#endif
break;
}
case tcCameraOwnerNameExif:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fOwnerName);
#if qDNGValidate
if (gVerbose)
{
printf ("CameraOwnerName: ");
DumpString (fOwnerName);
printf ("\n");
}
#endif
break;
}
case tcCameraSerialNumberExif:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fCameraSerialNumber);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (fCameraSerialNumber);
printf ("\n");
}
#endif
break;
}
case tcLensSpecificationExif:
{
CheckTagType (parentCode, tagCode, tagType, ttRational);
if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
return false;
fLensInfo [0] = stream.TagValue_urational (tagType);
fLensInfo [1] = stream.TagValue_urational (tagType);
fLensInfo [2] = stream.TagValue_urational (tagType);
fLensInfo [3] = stream.TagValue_urational (tagType);
// Some third party software wrote zero rather than undefined values for
// unknown entries. Work around this bug.
for (uint32 j = 0; j < 4; j++)
{
if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
{
fLensInfo [j] = dng_urational (0, 0);
#if qDNGValidate
ReportWarning ("Zero entry in LensSpecification tag--should be undefined");
#endif
}
}
#if qDNGValidate
if (gVerbose)
{
printf ("LensSpecificationExif: ");
real64 minFL = fLensInfo [0].As_real64 ();
real64 maxFL = fLensInfo [1].As_real64 ();
if (minFL == maxFL)
printf ("%0.1f mm", minFL);
else
printf ("%0.1f-%0.1f mm", minFL, maxFL);
if (fLensInfo [2].d)
{
real64 minFS = fLensInfo [2].As_real64 ();
real64 maxFS = fLensInfo [3].As_real64 ();
if (minFS == maxFS)
printf (" f/%0.1f", minFS);
else
printf (" f/%0.1f-%0.1f", minFS, maxFS);
}
printf ("\n");
}
#endif
break;
}
case tcLensMakeExif:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fLensMake);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (fLensMake);
printf ("\n");
}
#endif
break;
}
case tcLensModelExif:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fLensName);
fLensNameWasReadFromExif = fLensName.NotEmpty ();
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (fLensName);
printf ("\n");
}
#endif
break;
}
case tcLensSerialNumberExif:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fLensSerialNumber);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (fLensSerialNumber);
printf ("\n");
}
#endif
break;
}
default:
{
return false;
}
}
return true;
}
/*****************************************************************************/
// Parses tags that should only appear in GPS IFD
bool dng_exif::Parse_gps (dng_stream &stream,
dng_shared & /* shared */,
uint32 parentCode,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 /* tagOffset */)
{
switch (tagCode)
{
case tcGPSVersionID:
{
CheckTagType (parentCode, tagCode, tagType, ttByte);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fGPSVersionID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
printf ("GPSVersionID: %u.%u.%u.%u\n",
(unsigned) b0,
(unsigned) b1,
(unsigned) b2,
(unsigned) b3);
}
#endif
break;
}
case tcGPSLatitudeRef:
case tcGPSLongitudeRef:
case tcGPSSatellites:
case tcGPSStatus:
case tcGPSMeasureMode:
case tcGPSSpeedRef:
case tcGPSTrackRef:
case tcGPSImgDirectionRef:
case tcGPSMapDatum:
case tcGPSDestLatitudeRef:
case tcGPSDestLongitudeRef:
case tcGPSDestBearingRef:
case tcGPSDestDistanceRef:
case tcGPSDateStamp:
{
if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
return false;
dng_string *s;
switch (tagCode)
{
case tcGPSLatitudeRef:
s = &fGPSLatitudeRef;
break;
case tcGPSLongitudeRef:
s = &fGPSLongitudeRef;
break;
case tcGPSSatellites:
s = &fGPSSatellites;
break;
case tcGPSStatus:
s = &fGPSStatus;
break;
case tcGPSMeasureMode:
s = &fGPSMeasureMode;
break;
case tcGPSSpeedRef:
s = &fGPSSpeedRef;
break;
case tcGPSTrackRef:
s = &fGPSTrackRef;
break;
case tcGPSImgDirectionRef:
s = &fGPSImgDirectionRef;
break;
case tcGPSMapDatum:
s = &fGPSMapDatum;
break;
case tcGPSDestLatitudeRef:
s = &fGPSDestLatitudeRef;
break;
case tcGPSDestLongitudeRef:
s = &fGPSDestLongitudeRef;
break;
case tcGPSDestBearingRef:
s = &fGPSDestBearingRef;
break;
case tcGPSDestDistanceRef:
s = &fGPSDestDistanceRef;
break;
case tcGPSDateStamp:
s = &fGPSDateStamp;
break;
default:
return false;
}
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
*s);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (*s);
printf ("\n");
}
#endif
break;
}
case tcGPSLatitude:
case tcGPSLongitude:
case tcGPSTimeStamp:
case tcGPSDestLatitude:
case tcGPSDestLongitude:
{
if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
return false;
if (!CheckTagCount (parentCode, tagCode, tagCount, 3))
return false;
dng_urational *u;
switch (tagCode)
{
case tcGPSLatitude:
u = fGPSLatitude;
break;
case tcGPSLongitude:
u = fGPSLongitude;
break;
case tcGPSTimeStamp:
u = fGPSTimeStamp;
break;
case tcGPSDestLatitude:
u = fGPSDestLatitude;
break;
case tcGPSDestLongitude:
u = fGPSDestLongitude;
break;
default:
return false;
}
u [0] = stream.TagValue_urational (tagType);
u [1] = stream.TagValue_urational (tagType);
u [2] = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("%s:", LookupTagCode (parentCode, tagCode));
for (uint32 j = 0; j < 3; j++)
{
if (u [j].d == 0)
printf (" -");
else
printf (" %0.4f", u [j].As_real64 ());
}
printf ("\n");
}
#endif
break;
}
case tcGPSAltitudeRef:
{
CheckTagType (parentCode, tagCode, tagType, ttByte);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fGPSAltitudeRef = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("GPSAltitudeRef: ");
switch (fGPSAltitudeRef)
{
case 0:
printf ("Sea level");
break;
case 1:
printf ("Sea level reference (negative value)");
break;
default:
printf ("%u", (unsigned) fGPSAltitudeRef);
break;
}
printf ("\n");
}
#endif
break;
}
case tcGPSAltitude:
case tcGPSDOP:
case tcGPSSpeed:
case tcGPSTrack:
case tcGPSImgDirection:
case tcGPSDestBearing:
case tcGPSDestDistance:
case tcGPSHPositioningError:
{
if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
return false;
CheckTagCount (parentCode, tagCode, tagCount, 1);
dng_urational *u;
switch (tagCode)
{
case tcGPSAltitude:
u = &fGPSAltitude;
break;
case tcGPSDOP:
u = &fGPSDOP;
break;
case tcGPSSpeed:
u = &fGPSSpeed;
break;
case tcGPSTrack:
u = &fGPSTrack;
break;
case tcGPSImgDirection:
u = &fGPSImgDirection;
break;
case tcGPSDestBearing:
u = &fGPSDestBearing;
break;
case tcGPSDestDistance:
u = &fGPSDestDistance;
break;
case tcGPSHPositioningError:
u = &fGPSHPositioningError;
break;
default:
return false;
}
*u = stream.TagValue_urational (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("%s:", LookupTagCode (parentCode, tagCode));
if (u->d == 0)
printf (" -");
else
printf (" %0.4f", u->As_real64 ());
printf ("\n");
}
#endif
break;
}
case tcGPSProcessingMethod:
case tcGPSAreaInformation:
{
if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined))
return false;
dng_string *s;
switch (tagCode)
{
case tcGPSProcessingMethod:
s = &fGPSProcessingMethod;
break;
case tcGPSAreaInformation:
s = &fGPSAreaInformation;
break;
default:
return false;
}
ParseEncodedStringTag (stream,
parentCode,
tagCode,
tagCount,
*s);
#if qDNGValidate
if (gVerbose)
{
printf ("%s: ", LookupTagCode (parentCode, tagCode));
DumpString (*s);
printf ("\n");
}
#endif
break;
}
case tcGPSDifferential:
{
CheckTagType (parentCode, tagCode, tagType, ttShort);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fGPSDifferential = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("GPSDifferential: ");
switch (fGPSDifferential)
{
case 0:
printf ("Measurement without differential correction");
break;
case 1:
printf ("Differential correction applied");
break;
default:
printf ("%u", (unsigned) fGPSDifferential);
}
printf ("\n");
}
#endif
break;
}
default:
{
return false;
}
}
return true;
}
/*****************************************************************************/
// Parses tags that should only appear in Interoperability IFD
bool dng_exif::Parse_interoperability (dng_stream &stream,
dng_shared & /* shared */,
uint32 parentCode,
uint32 tagCode,
uint32 tagType,
uint32 tagCount,
uint64 /* tagOffset */)
{
switch (tagCode)
{
case tcInteroperabilityIndex:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
CheckTagCount (parentCode, tagCode, tagCount, 4);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fInteroperabilityIndex);
#if qDNGValidate
if (gVerbose)
{
printf ("InteroperabilityIndex: ");
DumpString (fInteroperabilityIndex);
printf ("\n");
}
#endif
break;
}
case tcInteroperabilityVersion:
{
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
CheckTagCount (parentCode, tagCode, tagCount, 4);
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
uint32 b3 = stream.Get_uint8 ();
fInteroperabilityVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
#if qDNGValidate
if (gVerbose)
{
real64 x = (b0 - '0') * 10.00 +
(b1 - '0') * 1.00 +
(b2 - '0') * 0.10 +
(b3 - '0') * 0.01;
printf ("InteroperabilityVersion: %0.2f\n", x);
}
#endif
break;
}
case tcRelatedImageFileFormat:
{
CheckTagType (parentCode, tagCode, tagType, ttAscii);
ParseStringTag (stream,
parentCode,
tagCode,
tagCount,
fRelatedImageFileFormat);
#if qDNGValidate
if (gVerbose)
{
printf ("RelatedImageFileFormat: ");
DumpString (fRelatedImageFileFormat);
printf ("\n");
}
#endif
break;
}
case tcRelatedImageWidth:
{
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fRelatedImageWidth = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("RelatedImageWidth: %u\n", (unsigned) fRelatedImageWidth);
}
#endif
break;
}
case tcRelatedImageLength:
{
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
CheckTagCount (parentCode, tagCode, tagCount, 1);
fRelatedImageLength = stream.TagValue_uint32 (tagType);
#if qDNGValidate
if (gVerbose)
{
printf ("RelatedImageLength: %u\n", (unsigned) fRelatedImageLength);
}
#endif
break;
}
default:
{
return false;
}
}
return true;
}
/*****************************************************************************/
void dng_exif::PostParse (dng_host & /* host */,
dng_shared & /* shared */)
{
#if qDNGValidate
const real64 kAPEX_Slop = 0.25;
// Sanity check on MaxApertureValue.
if (fMaxApertureValue.d)
{
real64 mav = fMaxApertureValue.As_real64 ();
// Compare against ApertureValue or FNumber.
real64 av = mav;
if (fApertureValue.d)
{
av = fApertureValue.As_real64 ();
}
else if (fFNumber.d)
{
real64 fs = fFNumber.As_real64 ();
if (fs >= 1.0)
{
av = FNumberToApertureValue (fs);
}
}
if (mav > av + kAPEX_Slop)
{
ReportWarning ("MaxApertureValue conflicts with ApertureValue and/or FNumber");
}
// Compare against LensInfo
if (fLensInfo [2].d && fLensInfo [3].d)
{
real64 fs1 = fLensInfo [2].As_real64 ();
real64 fs2 = fLensInfo [3].As_real64 ();
if (fs1 >= 1.0 && fs2 >= 1.0 && fs2 >= fs1)
{
real64 av1 = FNumberToApertureValue (fs1);
real64 av2 = FNumberToApertureValue (fs2);
// Wide angle adapters might create an effective
// wide FS, and tele-extenders always result
// in a higher FS.
if (mav < av1 - kAPEX_Slop - 1.0 ||
mav > av2 + kAPEX_Slop + 2.0)
{
ReportWarning ("Possible MaxApertureValue conflict with LensInfo");
}
}
}
}
// Sanity check on FocalLength.
if (fFocalLength.d)
{
real64 fl = fFocalLength.As_real64 ();
if (fl < 1.0)
{
ReportWarning ("FocalLength is less than 1.0 mm (legal but unlikely)");
}
else if (fLensInfo [0].d && fLensInfo [1].d)
{
real64 minFL = fLensInfo [0].As_real64 ();
real64 maxFL = fLensInfo [1].As_real64 ();
// Allow for wide-angle converters and tele-extenders.
if (fl < minFL * 0.6 ||
fl > maxFL * 2.1)
{
ReportWarning ("Possible FocalLength conflict with LensInfo");
}
}
}
#endif
// Mirror DateTimeOriginal to DateTime.
if (fDateTime.NotValid () && fDateTimeOriginal.IsValid ())
{
fDateTime = fDateTimeOriginal;
}
// Mirror EXIF 2.3 sensitivity tags to ISOSpeedRatings.
if (fISOSpeedRatings [0] == 0 || fISOSpeedRatings [0] == 65535)
{
// Prefer Recommended Exposure Index, then Standard Output Sensitivity, then
// ISO Speed, then Exposure Index.
if (fRecommendedExposureIndex != 0 &&
(fSensitivityType == stRecommendedExposureIndex ||
fSensitivityType == stSOSandREI ||
fSensitivityType == stREIandISOSpeed ||
fSensitivityType == stSOSandREIandISOSpeed))
{
fISOSpeedRatings [0] = fRecommendedExposureIndex;
}
else if (fStandardOutputSensitivity != 0 &&
(fSensitivityType == stStandardOutputSensitivity ||
fSensitivityType == stSOSandREI ||
fSensitivityType == stSOSandISOSpeed ||
fSensitivityType == stSOSandREIandISOSpeed))
{
fISOSpeedRatings [0] = fStandardOutputSensitivity;
}
else if (fISOSpeed != 0 &&
(fSensitivityType == stISOSpeed ||
fSensitivityType == stSOSandISOSpeed ||
fSensitivityType == stREIandISOSpeed ||
fSensitivityType == stSOSandREIandISOSpeed))
{
fISOSpeedRatings [0] = fISOSpeed;
}
}
// Mirror ExposureIndex to ISOSpeedRatings.
if (fExposureIndex.IsValid () && fISOSpeedRatings [0] == 0)
{
fISOSpeedRatings [0] = Round_uint32 (fExposureIndex.As_real64 ());
}
// Kodak sets the GPSAltitudeRef without setting the GPSAltitude.
if (fGPSAltitude.NotValid ())
{
fGPSAltitudeRef = 0xFFFFFFFF;
}
// If there is no valid GPS data, clear the GPS version number.
if (fGPSLatitude [0].NotValid () &&
fGPSLongitude [0].NotValid () &&
fGPSAltitude .NotValid () &&
fGPSTimeStamp [0].NotValid () &&
fGPSDateStamp .IsEmpty ())
{
fGPSVersionID = 0;
}
}
/*****************************************************************************/