You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4390 lines
73 KiB
4390 lines
73 KiB
/*****************************************************************************/ |
|
// 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; |
|
|
|
} |
|
|
|
} |
|
|
|
/*****************************************************************************/
|
|
|