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