/*****************************************************************************/ // 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; } } /*****************************************************************************/