| /*****************************************************************************/ |
| // Copyright 2006-2023 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. |
| /*****************************************************************************/ |
| |
| #include "dng_shared.h" |
| |
| #include "dng_camera_profile.h" |
| #include "dng_exceptions.h" |
| #include "dng_gain_map.h" |
| #include "dng_globals.h" |
| #include "dng_host.h" |
| #include "dng_memory.h" |
| #include "dng_parse_utils.h" |
| #include "dng_sdk_limits.h" |
| #include "dng_tag_codes.h" |
| #include "dng_tag_types.h" |
| #include "dng_tag_values.h" |
| #include "dng_temperature.h" |
| #include "dng_utils.h" |
| |
| #include <cstdio> |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile_dynamic_range::IsValid () const |
| { |
| |
| if (fVersion != 1) |
| return false; |
| |
| if (fDynamicRange >= 2) |
| return false; |
| |
| if (IsSDR () && fHintMaxOutputValue > 1.0f) |
| return false; |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile_dynamic_range::PutStream (dng_stream &stream) const |
| { |
| |
| stream.Put_uint16 (fVersion); |
| |
| stream.Put_uint16 (fDynamicRange); |
| |
| stream.Put_real32 (fHintMaxOutputValue); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #if qDNGValidate |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile_dynamic_range::Dump () const |
| { |
| |
| printf ("ProfileDynamicRange: version=%u, range=%s, hint_max=%g\n", |
| unsigned (fVersion), |
| IsHDR () ? "high" : "standard", |
| float (fHintMaxOutputValue)); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #endif // qDNGValidate |
| |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| |
| dng_camera_profile_info::dng_camera_profile_info () |
| |
| : fBigEndian (false) |
| |
| , fColorPlanes (0) |
| |
| , fCalibrationIlluminant1 (lsUnknown) |
| , fCalibrationIlluminant2 (lsUnknown) |
| , fCalibrationIlluminant3 (lsUnknown) |
| |
| , fColorMatrix1 () |
| , fColorMatrix2 () |
| , fColorMatrix3 () |
| |
| , fForwardMatrix1 () |
| , fForwardMatrix2 () |
| , fForwardMatrix3 () |
| |
| , fReductionMatrix1 () |
| , fReductionMatrix2 () |
| , fReductionMatrix3 () |
| |
| , fProfileCalibrationSignature () |
| |
| , fProfileName () |
| |
| , fProfileCopyright () |
| |
| , fEmbedPolicy (pepAllowCopying) |
| |
| , fProfileHues (0) |
| , fProfileSats (0) |
| , fProfileVals (0) |
| |
| , fHueSatDeltas1Offset (0) |
| , fHueSatDeltas1Count (0) |
| |
| , fHueSatDeltas2Offset (0) |
| , fHueSatDeltas2Count (0) |
| |
| , fHueSatDeltas3Offset (0) |
| , fHueSatDeltas3Count (0) |
| |
| , fHueSatMapEncoding (encoding_Linear) |
| |
| , fLookTableHues (0) |
| , fLookTableSats (0) |
| , fLookTableVals (0) |
| |
| , fLookTableOffset (0) |
| , fLookTableCount (0) |
| |
| , fLookTableEncoding (encoding_Linear) |
| |
| , fBaselineExposureOffset (0, 100) |
| |
| , fDefaultBlackRender (defaultBlackRender_Auto) |
| |
| , fToneCurveOffset (0) |
| , fToneCurveCount (0) |
| |
| , fToneMethod (profileToneMethod_Unspecified) |
| |
| , fUniqueCameraModel () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_camera_profile_info::~dng_camera_profile_info () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") |
| bool dng_camera_profile_info::ParseTag (dng_stream &stream, |
| uint32 parentCode, |
| uint32 tagCode, |
| uint32 tagType, |
| uint32 tagCount, |
| uint64 tagOffset) |
| { |
| |
| switch (tagCode) |
| { |
| |
| case tcCalibrationIlluminant1: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fCalibrationIlluminant1 = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CalibrationIlluminant1: %s\n", |
| LookupLightSource (fCalibrationIlluminant1)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCalibrationIlluminant2: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fCalibrationIlluminant2 = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CalibrationIlluminant2: %s\n", |
| LookupLightSource (fCalibrationIlluminant2)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCalibrationIlluminant3: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fCalibrationIlluminant3 = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CalibrationIlluminant3: %s\n", |
| LookupLightSource (fCalibrationIlluminant3)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcIlluminantData1: |
| case tcIlluminantData2: |
| case tcIlluminantData3: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) |
| { |
| return false; |
| } |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 10000)) |
| { |
| return false; |
| } |
| |
| dng_illuminant_data *dstPtr = &fIlluminantData1; |
| |
| if (tagCode == tcIlluminantData2) |
| { |
| dstPtr = &fIlluminantData2; |
| } |
| |
| else if (tagCode == tcIlluminantData3) |
| { |
| dstPtr = &fIlluminantData3; |
| } |
| |
| #if qDNGValidate |
| const char *tagName = LookupTagCode (parentCode, tagCode); |
| #else |
| const char *tagName = ""; |
| #endif |
| |
| dstPtr->Get (stream, |
| tagCount, |
| tagName); |
| |
| break; |
| |
| } |
| |
| case tcColorMatrix1: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (fColorPlanes == 0) |
| { |
| |
| fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); |
| |
| } |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fColorPlanes, |
| 3, |
| fColorMatrix1)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ColorMatrix1:\n"); |
| |
| DumpMatrix (fColorMatrix1); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcColorMatrix2: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| // Kludge - Hasselblad FFF files are very DNG-like, but sometimes |
| // only have a ColorMatrix2 tag and no ColorMatrix1 tag. |
| |
| bool hasselbladHack = (fColorPlanes == 0); |
| |
| if (hasselbladHack) |
| { |
| |
| fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); |
| |
| #if qDNGValidate |
| |
| ReportWarning ("ColorMatrix2 without ColorMatrix1"); |
| |
| #endif |
| |
| } |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fColorPlanes, |
| 3, |
| fColorMatrix2)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ColorMatrix2:\n"); |
| |
| DumpMatrix (fColorMatrix2); |
| |
| } |
| |
| #endif |
| |
| if (hasselbladHack) |
| { |
| |
| fColorMatrix1 = fColorMatrix2; |
| |
| fColorMatrix2 = dng_matrix (); |
| |
| } |
| |
| break; |
| |
| } |
| |
| case tcColorMatrix3: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (fColorPlanes == 0) |
| { |
| |
| fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); |
| |
| } |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fColorPlanes, |
| 3, |
| fColorMatrix3)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ColorMatrix3:\n"); |
| |
| DumpMatrix (fColorMatrix3); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcForwardMatrix1: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fForwardMatrix1)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ForwardMatrix1:\n"); |
| |
| DumpMatrix (fForwardMatrix1); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcForwardMatrix2: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fForwardMatrix2)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ForwardMatrix2:\n"); |
| |
| DumpMatrix (fForwardMatrix2); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcForwardMatrix3: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fForwardMatrix3)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ForwardMatrix3:\n"); |
| |
| DumpMatrix (fForwardMatrix3); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcReductionMatrix1: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fReductionMatrix1)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ReductionMatrix1:\n"); |
| |
| DumpMatrix (fReductionMatrix1); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcReductionMatrix2: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fReductionMatrix2)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ReductionMatrix2:\n"); |
| |
| DumpMatrix (fReductionMatrix2); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcReductionMatrix3: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| 3, |
| fColorPlanes, |
| fReductionMatrix3)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ReductionMatrix3:\n"); |
| |
| DumpMatrix (fReductionMatrix3); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileCalibrationSignature: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fProfileCalibrationSignature, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileCalibrationSignature: "); |
| |
| DumpString (fProfileCalibrationSignature); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileName: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fProfileName, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileName: "); |
| |
| DumpString (fProfileName); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileCopyright: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fProfileCopyright, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileCopyright: "); |
| |
| DumpString (fProfileCopyright); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileEmbedPolicy: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fEmbedPolicy = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| const char *policy; |
| |
| switch (fEmbedPolicy) |
| { |
| |
| case pepAllowCopying: |
| policy = "Allow copying"; |
| break; |
| |
| case pepEmbedIfUsed: |
| policy = "Embed if used"; |
| break; |
| |
| case pepEmbedNever: |
| policy = "Embed never"; |
| break; |
| |
| case pepNoRestrictions: |
| policy = "No restrictions"; |
| break; |
| |
| default: |
| policy = "INVALID VALUE"; |
| |
| } |
| |
| printf ("ProfileEmbedPolicy: %s\n", policy); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileHueSatMapDims: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 2, 3); |
| |
| fProfileHues = stream.TagValue_uint32 (tagType); |
| fProfileSats = stream.TagValue_uint32 (tagType); |
| |
| if (tagCount > 2) |
| fProfileVals = stream.TagValue_uint32 (tagType); |
| else |
| fProfileVals = 1; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileHueSatMapDims: Hues = %u, Sats = %u, Vals = %u\n", |
| (unsigned) fProfileHues, |
| (unsigned) fProfileSats, |
| (unsigned) fProfileVals); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileHueSatMapData1: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) |
| return false; |
| |
| if (fProfileSats == 0) |
| return false; |
| |
| dng_safe_uint32 hueCount (fProfileHues); |
| dng_safe_uint32 valCount (fProfileVals); |
| |
| bool skipSat0 = (tagCount == (hueCount * |
| (fProfileSats - 1) * |
| (valCount * 3u)).Get ()); |
| |
| if (!skipSat0) |
| { |
| |
| dng_safe_uint32 expected = hueCount * valCount * fProfileSats * 3u; |
| |
| if (!CheckTagCount (parentCode, |
| tagCode, |
| tagCount, |
| expected.Get ())) |
| { |
| return false; |
| } |
| |
| } |
| |
| fBigEndian = stream.BigEndian (); |
| |
| fHueSatDeltas1Offset = tagOffset; |
| fHueSatDeltas1Count = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileHueSatMapData1:\n"); |
| |
| DumpHueSatMap (stream, |
| fProfileHues, |
| fProfileSats, |
| fProfileVals, |
| skipSat0); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileHueSatMapData2: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) |
| return false; |
| |
| if (fProfileSats == 0) |
| return false; |
| |
| dng_safe_uint32 hueCount (fProfileHues); |
| dng_safe_uint32 valCount (fProfileVals); |
| |
| bool skipSat0 = (tagCount == (hueCount * |
| (fProfileSats - 1) * |
| (valCount * 3u)).Get ()); |
| |
| if (!skipSat0) |
| { |
| |
| dng_safe_uint32 expected = hueCount * valCount * fProfileSats * 3u; |
| |
| if (!CheckTagCount (parentCode, |
| tagCode, |
| tagCount, |
| expected.Get ())) |
| { |
| return false; |
| } |
| |
| } |
| |
| fBigEndian = stream.BigEndian (); |
| |
| fHueSatDeltas2Offset = tagOffset; |
| fHueSatDeltas2Count = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileHueSatMapData2:\n"); |
| |
| DumpHueSatMap (stream, |
| fProfileHues, |
| fProfileSats, |
| fProfileVals, |
| skipSat0); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileHueSatMapData3: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) |
| return false; |
| |
| if (fProfileSats == 0) |
| return false; |
| |
| dng_safe_uint32 hueCount (fProfileHues); |
| dng_safe_uint32 valCount (fProfileVals); |
| |
| bool skipSat0 = (tagCount == (hueCount * |
| (fProfileSats - 1) * |
| (valCount * 3u)).Get ()); |
| |
| if (!skipSat0) |
| { |
| |
| dng_safe_uint32 expected = hueCount * valCount * fProfileSats * 3u; |
| |
| if (!CheckTagCount (parentCode, |
| tagCode, |
| tagCount, |
| expected.Get ())) |
| { |
| return false; |
| } |
| |
| } |
| |
| fBigEndian = stream.BigEndian (); |
| |
| fHueSatDeltas3Offset = tagOffset; |
| fHueSatDeltas3Count = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileHueSatMapData3:\n"); |
| |
| DumpHueSatMap (stream, |
| fProfileHues, |
| fProfileSats, |
| fProfileVals, |
| skipSat0); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileHueSatMapEncoding: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fHueSatMapEncoding = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| const char *encoding = NULL; |
| |
| switch (fHueSatMapEncoding) |
| { |
| |
| case encoding_Linear: |
| encoding = "Linear"; |
| break; |
| |
| case encoding_sRGB: |
| encoding = "sRGB"; |
| break; |
| |
| default: |
| encoding = "INVALID VALUE"; |
| |
| } |
| |
| printf ("ProfileHueSatMapEncoding: %s\n", encoding); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileLookTableDims: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 2, 3); |
| |
| fLookTableHues = stream.TagValue_uint32 (tagType); |
| fLookTableSats = stream.TagValue_uint32 (tagType); |
| |
| if (tagCount > 2) |
| fLookTableVals = stream.TagValue_uint32 (tagType); |
| else |
| fLookTableVals = 1; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileLookTableDims: Hues = %u, Sats = %u, Vals = %u\n", |
| (unsigned) fLookTableHues, |
| (unsigned) fLookTableSats, |
| (unsigned) fLookTableVals); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileLookTableData: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) |
| return false; |
| |
| if (fLookTableSats == 0) |
| return false; |
| |
| dng_safe_uint32 hueCount (fLookTableHues); |
| dng_safe_uint32 valCount (fLookTableVals); |
| |
| bool skipSat0 = (tagCount == (hueCount * |
| (fLookTableSats - 1) * |
| valCount * 3u).Get ()); |
| |
| if (!skipSat0) |
| { |
| |
| dng_safe_uint32 expected = hueCount * valCount * fLookTableSats * 3u; |
| |
| if (!CheckTagCount (parentCode, |
| tagCode, |
| tagCount, |
| expected.Get ())) |
| { |
| return false; |
| } |
| |
| } |
| |
| fBigEndian = stream.BigEndian (); |
| |
| fLookTableOffset = tagOffset; |
| fLookTableCount = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileLookTableData:\n"); |
| |
| DumpHueSatMap (stream, |
| fLookTableHues, |
| fLookTableSats, |
| fLookTableVals, |
| skipSat0); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileLookTableEncoding: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fLookTableEncoding = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| const char *encoding = NULL; |
| |
| switch (fLookTableEncoding) |
| { |
| |
| case encoding_Linear: |
| encoding = "Linear"; |
| break; |
| |
| case encoding_sRGB: |
| encoding = "sRGB"; |
| break; |
| |
| default: |
| encoding = "INVALID VALUE"; |
| |
| } |
| |
| printf ("ProfileLookTableEncoding: %s\n", encoding); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBaselineExposureOffset: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fBaselineExposureOffset = stream.TagValue_srational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BaselineExposureOffset: %+0.2f\n", |
| fBaselineExposureOffset.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDefaultBlackRender: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDefaultBlackRender = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| const char *setting = NULL; |
| |
| switch (fDefaultBlackRender) |
| { |
| |
| case defaultBlackRender_Auto: |
| setting = "Auto"; |
| break; |
| |
| case defaultBlackRender_None: |
| setting = "None"; |
| break; |
| |
| default: |
| setting = "INVALID VALUE"; |
| |
| } |
| |
| printf ("DefaultBlackRender: %s\n", |
| setting); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileToneCurve: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 4, tagCount)) |
| return false; |
| |
| if ((tagCount & 1) != 0) |
| { |
| |
| #if qDNGValidate |
| |
| { |
| |
| char message [256]; |
| |
| snprintf (message, |
| 256, |
| "%s %s has odd count (%u)", |
| LookupParentCode (parentCode), |
| LookupTagCode (parentCode, tagCode), |
| (unsigned) tagCount); |
| |
| ReportWarning (message); |
| |
| } |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| fBigEndian = stream.BigEndian (); |
| |
| fToneCurveOffset = tagOffset; |
| fToneCurveCount = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| DumpTagValues (stream, |
| "Coord", |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount); |
| |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileToneMethod: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fToneMethod = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| const char *setting = NULL; |
| |
| switch (fToneMethod) |
| { |
| |
| case profileToneMethod_Unspecified: |
| setting = "Unspecified"; |
| break; |
| |
| case profileToneMethod_AdobePV5: |
| setting = "Adobe PV5"; |
| break; |
| |
| case profileToneMethod_AdobePV6: |
| setting = "Adobe PV6"; |
| break; |
| |
| default: |
| setting = "INVALID VALUE"; |
| |
| } |
| |
| printf ("ProfileToneMethod: %s\n", |
| setting); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcUniqueCameraModel: |
| { |
| |
| // Note: This code is only used when parsing stand-alone |
| // profiles. The embedded profiles are assumed to be restricted |
| // to the model they are embedded in. |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fUniqueCameraModel, |
| false); |
| |
| bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); |
| |
| #if qDNGValidate |
| |
| if (didTrim) |
| { |
| |
| ReportWarning ("UniqueCameraModel string has trailing blanks"); |
| |
| } |
| |
| if (gVerbose) |
| { |
| |
| printf ("UniqueCameraModel: "); |
| |
| DumpString (fUniqueCameraModel); |
| |
| printf ("\n"); |
| |
| } |
| |
| #else |
| |
| (void) didTrim; // Unused |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileGainTableMap2: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) |
| return false; |
| |
| // For Camera Profile IFD, only ProfileGainTableMap2 is permitted; |
| // not the original ProfileGainTableMap. |
| |
| constexpr bool useVersion2format = true; |
| |
| dng_host host; // use default allocator |
| |
| fProfileGainTableMap.reset |
| (dng_gain_table_map::GetStream (host, |
| stream, |
| useVersion2format)); |
| |
| auto pgtm = fProfileGainTableMap; |
| |
| #if qDNGValidate |
| |
| if (pgtm && gVerbose) |
| { |
| |
| dng_md5_printer_le_stream printer; |
| |
| pgtm->AddDigest (printer); |
| |
| auto digest = printer.Result (); |
| |
| char str [2 * dng_fingerprint::kDNGFingerprintSize + 1]; |
| |
| digest.ToUtf8HexString (str); |
| |
| printf ("ProfileGainTableMap2 (digest): %s\n", str); |
| |
| } |
| |
| #endif // qDNGValidate |
| |
| if (stream.Position () > tagOffset + (uint64) tagCount) |
| ThrowBadFormat ("tcProfileGainTableMap2 parse error"); |
| |
| break; |
| |
| } |
| |
| case tcProfileDynamicRange: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 8)) |
| return false; |
| |
| uint16 version = stream.Get_uint16 (); |
| |
| if (version == 1) |
| { |
| |
| fProfileDynamicRange.fVersion = version; |
| |
| fProfileDynamicRange.fDynamicRange = stream.Get_uint16 (); |
| |
| fProfileDynamicRange.fHintMaxOutputValue = stream.Get_real32 (); |
| |
| } |
| |
| else |
| { |
| |
| ThrowBadFormat ("Unsupported version in ProfileDynamicRange"); |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| fProfileDynamicRange.Dump (); |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcProfileGroupName: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fProfileGroupName, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ProfileGroupName: "); |
| |
| DumpString (fProfileGroupName); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif // qDNGValidate |
| |
| break; |
| |
| } |
| |
| case tcRGBTablesDraft: |
| case tcRGBTables: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) |
| return false; |
| |
| const bool isDraft = (tagCode == tcRGBTablesDraft); |
| |
| dng_host host; |
| |
| fMaskedRGBTables.reset (dng_masked_rgb_tables::GetStream (host, |
| stream, |
| isDraft)); |
| |
| #if qDNGValidate |
| |
| if (gVerbose && fMaskedRGBTables) |
| { |
| |
| dng_md5_printer_le_stream printer; |
| |
| fMaskedRGBTables->AddDigest (printer); |
| |
| const dng_fingerprint &digest = printer.Result (); |
| |
| dng_string str = digest.ToUtf8HexString (); |
| |
| const char *tagName = isDraft ? "RGBTablesDraft" : "RGBTables"; |
| |
| printf ("%s (digest): %s\n", tagName, str.Get ()); |
| |
| fMaskedRGBTables->Dump (); |
| |
| } |
| |
| #endif // qDNGValidate |
| |
| if (stream.Position () > tagOffset + (uint64) tagCount) |
| { |
| |
| ThrowBadFormat ("tcRGBTables parse error"); |
| |
| } |
| |
| break; |
| |
| } |
| |
| default: |
| { |
| |
| return false; |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile_info::ParseExtended (dng_stream &stream) |
| { |
| |
| try |
| { |
| |
| // Offsets are relative to the start of this structure, not the entire file. |
| |
| uint64 startPosition = stream.Position (); |
| |
| // Read header. Like a TIFF header, but with different magic number |
| // Plus all offsets are relative to the start of the IFD, not to the |
| // stream or file. |
| |
| uint16 byteOrder = stream.Get_uint16 (); |
| |
| if (byteOrder == byteOrderMM) |
| fBigEndian = true; |
| |
| else if (byteOrder == byteOrderII) |
| fBigEndian = false; |
| |
| else |
| return false; |
| |
| TempBigEndian setEndianness (stream, fBigEndian); |
| |
| uint16 magicNumber = stream.Get_uint16 (); |
| |
| if (magicNumber != magicExtendedProfile) |
| { |
| return false; |
| } |
| |
| uint32 offset = stream.Get_uint32 (); |
| |
| stream.Skip (SafeUint32Sub (offset, 8u)); |
| |
| // Start on IFD entries. |
| |
| uint32 ifdEntries = stream.Get_uint16 (); |
| |
| if (ifdEntries < 1) |
| { |
| return false; |
| } |
| |
| for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) |
| { |
| |
| stream.SetReadPosition (startPosition + 8 + 2 + tag_index * 12); |
| |
| uint16 tagCode = stream.Get_uint16 (); |
| uint32 tagType = stream.Get_uint16 (); |
| uint32 tagCount = stream.Get_uint32 (); |
| |
| uint64 tagOffset = stream.Position (); |
| |
| if (SafeUint32Mult (TagTypeSize (tagType), tagCount) > 4) |
| { |
| |
| tagOffset = startPosition + stream.Get_uint32 (); |
| |
| stream.SetReadPosition (tagOffset); |
| |
| } |
| |
| if (!ParseTag (stream, |
| 0, |
| tagCode, |
| tagType, |
| tagCount, |
| tagOffset)) |
| { |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| stream.SetReadPosition (tagOffset); |
| |
| printf ("*"); |
| |
| DumpTagValues (stream, |
| LookupTagType (tagType), |
| 0, |
| tagCode, |
| tagType, |
| tagCount); |
| |
| } |
| |
| #endif |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| catch (...) |
| { |
| |
| // Eat parsing errors. |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_shared::dng_shared () |
| |
| : fExifIFD (0) |
| , fGPSInfo (0) |
| , fInteroperabilityIFD (0) |
| , fKodakDCRPrivateIFD (0) |
| , fKodakKDCPrivateIFD (0) |
| |
| , fXMPCount (0) |
| , fXMPOffset (0) |
| |
| , fIPTC_NAA_Count (0) |
| , fIPTC_NAA_Offset (0) |
| |
| , fMakerNoteCount (0) |
| , fMakerNoteOffset (0) |
| , fMakerNoteSafety (0) |
| |
| , fDNGVersion (0) |
| , fDNGBackwardVersion (0) |
| |
| , fUniqueCameraModel () |
| , fLocalizedCameraModel () |
| |
| , fCameraProfile () |
| |
| , fExtraCameraProfiles () |
| |
| , fCameraCalibration1 () |
| , fCameraCalibration2 () |
| , fCameraCalibration3 () |
| |
| , fCameraCalibrationSignature () |
| |
| , fAnalogBalance () |
| |
| , fAsShotNeutral () |
| |
| , fAsShotWhiteXY () |
| |
| , fBaselineExposure (0, 1) |
| , fBaselineNoise (1, 1) |
| , fBaselineSharpness (1, 1) |
| , fLinearResponseLimit (1, 1) |
| , fShadowScale (1, 1) |
| |
| , fHasBaselineExposure (false) |
| , fHasShadowScale (false) |
| |
| , fDNGPrivateDataCount (0) |
| , fDNGPrivateDataOffset (0) |
| |
| , fRawImageDigest () |
| , fNewRawImageDigest () |
| |
| , fRawDataUniqueID () |
| |
| , fOriginalRawFileName () |
| |
| , fOriginalRawFileDataCount (0) |
| , fOriginalRawFileDataOffset (0) |
| |
| , fOriginalRawFileDigest () |
| |
| , fAsShotICCProfileCount (0) |
| , fAsShotICCProfileOffset (0) |
| |
| , fAsShotPreProfileMatrix () |
| |
| , fCurrentICCProfileCount (0) |
| , fCurrentICCProfileOffset (0) |
| |
| , fCurrentPreProfileMatrix () |
| |
| , fColorimetricReference (crSceneReferred) |
| |
| , fAsShotProfileName () |
| |
| , fOriginalDefaultFinalSize () |
| , fOriginalBestQualityFinalSize () |
| |
| , fOriginalDefaultCropSizeH () |
| , fOriginalDefaultCropSizeV () |
| |
| , fDepthFormat (depthFormatUnknown) |
| , fDepthNear (0, 0) |
| , fDepthFar (0, 0) |
| , fDepthUnits (depthUnitsUnknown) |
| , fDepthMeasureType (depthMeasureUnknown) |
| |
| , fBigTableDigests () |
| , fBigTableOffsets () |
| , fBigTableByteCounts () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_shared::~dng_shared () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_shared::ParseTag (dng_stream &stream, |
| dng_exif &exif, |
| uint32 parentCode, |
| bool /* isMainIFD */, |
| uint32 tagCode, |
| uint32 tagType, |
| uint32 tagCount, |
| uint64 tagOffset, |
| int64 /* offsetDelta */) |
| { |
| |
| if (parentCode == 0) |
| { |
| |
| if (Parse_ifd0 (stream, |
| exif, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| tagOffset)) |
| { |
| |
| return true; |
| |
| } |
| |
| } |
| |
| if (parentCode == 0 || |
| parentCode == tcExifIFD) |
| { |
| |
| if (Parse_ifd0_exif (stream, |
| exif, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| tagOffset)) |
| { |
| |
| return true; |
| |
| } |
| |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| // Parses tags that should only appear in IFD 0. |
| |
| bool dng_shared::Parse_ifd0 (dng_stream &stream, |
| dng_exif & /* exif */, |
| uint32 parentCode, |
| uint32 tagCode, |
| uint32 tagType, |
| uint32 tagCount, |
| uint64 tagOffset) |
| { |
| |
| switch (tagCode) |
| { |
| |
| case tcXMP: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttByte, ttUndefined); |
| |
| fXMPCount = tagCount; |
| fXMPOffset = fXMPCount ? tagOffset : 0; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("XMP: Count = %u, Offset = %u\n", |
| (unsigned) fXMPCount, |
| (unsigned) fXMPOffset); |
| |
| if (fXMPCount) |
| { |
| |
| DumpXMP (stream, fXMPCount); |
| |
| } |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcIPTC_NAA: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttAscii, ttUndefined); |
| |
| fIPTC_NAA_Count = (dng_safe_uint32 (tagCount) * TagTypeSize (tagType)).Get (); |
| fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("IPTC/NAA: Count = %u, Offset = %llu\n", |
| (unsigned) fIPTC_NAA_Count, |
| (unsigned long long) fIPTC_NAA_Offset); |
| |
| if (fIPTC_NAA_Count) |
| { |
| |
| DumpHexAscii (stream, fIPTC_NAA_Count); |
| |
| } |
| |
| // Compute and output the digest. |
| |
| dng_memory_data buffer (fIPTC_NAA_Count); |
| |
| stream.SetReadPosition (fIPTC_NAA_Offset); |
| |
| stream.Get (buffer.Buffer (), fIPTC_NAA_Count); |
| |
| const uint8 *data = buffer.Buffer_uint8 (); |
| |
| uint32 count = fIPTC_NAA_Count; |
| |
| // Method 1: Counting all bytes (this is correct). |
| |
| { |
| |
| dng_md5_direct_printer printer; |
| |
| printer.ProcessPtr (data, count); |
| |
| printf ("IPTCDigest: "); |
| |
| DumpFingerprint (printer.Result ()); |
| |
| printf ("\n"); |
| |
| } |
| |
| // Method 2: Ignoring zero padding. |
| |
| { |
| |
| uint32 removed = 0; |
| |
| while ((removed < 3) && (count > 0) && (data [count - 1] == 0)) |
| { |
| removed++; |
| count--; |
| } |
| |
| if (removed != 0) |
| { |
| |
| dng_md5_direct_printer printer; |
| |
| printer.ProcessPtr (data, count); |
| |
| printf ("IPTCDigest (ignoring zero padding): "); |
| |
| DumpFingerprint (printer.Result ()); |
| |
| printf ("\n"); |
| |
| } |
| |
| } |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcExifIFD: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD, ttLong8, ttIFD8); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fExifIFD = stream.TagValue_uint64 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("ExifIFD: %llu\n", (unsigned long long) fExifIFD); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcGPSInfo: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD, ttLong8, ttIFD8); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fGPSInfo = stream.TagValue_uint64 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("GPSInfo: %llu\n", (unsigned long long) fGPSInfo); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcKodakDCRPrivateIFD: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fKodakDCRPrivateIFD = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("KodakDCRPrivateIFD: %u\n", (unsigned) fKodakDCRPrivateIFD); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcKodakKDCPrivateIFD: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fKodakKDCPrivateIFD = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("KodakKDCPrivateIFD: %u\n", (unsigned) fKodakKDCPrivateIFD); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcC2PAManifest: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttUndefined); |
| |
| fC2PAManifestOffset = stream.Position (); |
| fC2PAManifestCount = tagCount; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("C2PAManifest: offset = %llu, count = %u\n", |
| (unsigned long long) fC2PAManifestOffset, |
| (unsigned) fC2PAManifestCount); |
| |
| DumpHexAscii (stream, fC2PAManifestCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDNGVersion: |
| { |
| |
| 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 (); |
| |
| fDNGVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("DNGVersion: %u.%u.%u.%u\n", |
| (unsigned) b0, |
| (unsigned) b1, |
| (unsigned) b2, |
| (unsigned) b3); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDNGBackwardVersion: |
| { |
| |
| 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 (); |
| |
| fDNGBackwardVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("DNGBackwardVersion: %u.%u.%u.%u\n", |
| (unsigned) b0, |
| (unsigned) b1, |
| (unsigned) b2, |
| (unsigned) b3); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcUniqueCameraModel: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fUniqueCameraModel, |
| false); |
| |
| bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); |
| |
| #if qDNGValidate |
| |
| if (didTrim) |
| { |
| |
| ReportWarning ("UniqueCameraModel string has trailing blanks"); |
| |
| } |
| |
| if (gVerbose) |
| { |
| |
| printf ("UniqueCameraModel: "); |
| |
| DumpString (fUniqueCameraModel); |
| |
| printf ("\n"); |
| |
| } |
| |
| #else |
| |
| (void) didTrim; // Unused |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcLocalizedCameraModel: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fLocalizedCameraModel, |
| false); |
| |
| bool didTrim = fLocalizedCameraModel.TrimTrailingBlanks (); |
| |
| #if qDNGValidate |
| |
| if (didTrim) |
| { |
| |
| ReportWarning ("LocalizedCameraModel string has trailing blanks"); |
| |
| } |
| |
| if (gVerbose) |
| { |
| |
| printf ("LocalizedCameraModel: "); |
| |
| DumpString (fLocalizedCameraModel); |
| |
| printf ("\n"); |
| |
| } |
| |
| #else |
| |
| (void) didTrim; // Unused |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCameraCalibration1: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fCameraProfile.fColorPlanes, |
| fCameraProfile.fColorPlanes, |
| fCameraCalibration1)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CameraCalibration1:\n"); |
| |
| DumpMatrix (fCameraCalibration1); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCameraCalibration2: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fCameraProfile.fColorPlanes, |
| fCameraProfile.fColorPlanes, |
| fCameraCalibration2)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CameraCalibration2:\n"); |
| |
| DumpMatrix (fCameraCalibration2); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCameraCalibration3: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fCameraProfile.fColorPlanes, |
| fCameraProfile.fColorPlanes, |
| fCameraCalibration3)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CameraCalibration3:\n"); |
| |
| DumpMatrix (fCameraCalibration3); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCameraCalibrationSignature: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fCameraCalibrationSignature, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CameraCalibrationSignature: "); |
| |
| DumpString (fCameraCalibrationSignature); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAnalogBalance: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| // Kludge - Hasselblad FFF files are very DNG-like, but sometimes |
| // they don't have any ColorMatrix tags. |
| |
| bool hasselbladHack = (fDNGVersion == 0 && |
| fCameraProfile.fColorPlanes == 0); |
| |
| if (hasselbladHack) |
| { |
| |
| fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); |
| |
| #if qDNGValidate |
| |
| ReportWarning ("AnalogBalance without ColorMatrix1"); |
| |
| #endif |
| |
| } |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!ParseVectorTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fCameraProfile.fColorPlanes, |
| fAnalogBalance)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AnalogBalance:"); |
| |
| DumpVector (fAnalogBalance); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAsShotNeutral: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| // Kludge - Hasselblad FFF files are very DNG-like, but sometimes |
| // they don't have any ColorMatrix tags. |
| |
| bool hasselbladHack = (fDNGVersion == 0 && |
| fCameraProfile.fColorPlanes == 0); |
| |
| if (hasselbladHack) |
| { |
| |
| fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); |
| |
| #if qDNGValidate |
| |
| ReportWarning ("AsShotNeutral without ColorMatrix1"); |
| |
| #endif |
| |
| } |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!ParseVectorTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| fCameraProfile.fColorPlanes, |
| fAsShotNeutral)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AsShotNeutral:"); |
| |
| DumpVector (fAsShotNeutral); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAsShotWhiteXY: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) |
| return false; |
| |
| fAsShotWhiteXY.x = stream.TagValue_real64 (tagType); |
| fAsShotWhiteXY.y = stream.TagValue_real64 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AsShotWhiteXY: %0.4f %0.4f\n", |
| fAsShotWhiteXY.x, |
| fAsShotWhiteXY.y); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBaselineExposure: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fBaselineExposure = stream.TagValue_srational (tagType); |
| |
| fHasBaselineExposure = true; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BaselineExposure: %+0.2f\n", |
| fBaselineExposure.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBaselineNoise: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fBaselineNoise = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BaselineNoise: %0.2f\n", |
| fBaselineNoise.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBaselineSharpness: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fBaselineSharpness = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BaselineSharpness: %0.2f\n", |
| fBaselineSharpness.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcLinearResponseLimit: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fLinearResponseLimit = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("LinearResponseLimit: %0.2f\n", |
| fLinearResponseLimit.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcShadowScale: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fShadowScale = stream.TagValue_urational (tagType); |
| |
| fHasShadowScale = true; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ShadowScale: %0.4f\n", |
| fShadowScale.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDNGPrivateData: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttByte); |
| |
| fDNGPrivateDataCount = tagCount; |
| fDNGPrivateDataOffset = tagOffset; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DNGPrivateData: Count = %u, Offset = %u\n", |
| (unsigned) fDNGPrivateDataCount, |
| (unsigned) fDNGPrivateDataOffset); |
| |
| DumpHexAscii (stream, tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcMakerNoteSafety: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fMakerNoteSafety = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("MakerNoteSafety: %s\n", |
| LookupMakerNoteSafety (fMakerNoteSafety)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcRawImageDigest: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) |
| return false; |
| |
| stream.Get (fRawImageDigest); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("RawImageDigest: "); |
| |
| DumpFingerprint (fRawImageDigest); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcNewRawImageDigest: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) |
| return false; |
| |
| stream.Get (fNewRawImageDigest); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("NewRawImageDigest: "); |
| |
| DumpFingerprint (fNewRawImageDigest); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcRawDataUniqueID: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) |
| return false; |
| |
| stream.Get (fRawDataUniqueID); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("RawDataUniqueID: "); |
| |
| DumpFingerprint (fRawDataUniqueID); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalRawFileName: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fOriginalRawFileName, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalRawFileName: "); |
| |
| DumpString (fOriginalRawFileName); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalRawFileData: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttUndefined); |
| |
| fOriginalRawFileDataCount = tagCount; |
| fOriginalRawFileDataOffset = tagOffset; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalRawFileData: Count = %u, Offset = %u\n", |
| (unsigned) fOriginalRawFileDataCount, |
| (unsigned) fOriginalRawFileDataOffset); |
| |
| DumpHexAscii (stream, tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalRawFileDigest: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) |
| return false; |
| |
| stream.Get (fOriginalRawFileDigest); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalRawFileDigest: "); |
| |
| DumpFingerprint (fOriginalRawFileDigest); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAsShotICCProfile: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttUndefined); |
| |
| fAsShotICCProfileCount = tagCount; |
| fAsShotICCProfileOffset = tagOffset; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AsShotICCProfile: Count = %u, Offset = %u\n", |
| (unsigned) fAsShotICCProfileCount, |
| (unsigned) fAsShotICCProfileOffset); |
| |
| DumpHexAscii (stream, tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAsShotPreProfileMatrix: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| uint32 rows = fCameraProfile.fColorPlanes; |
| |
| if (tagCount == fCameraProfile.fColorPlanes * 3) |
| { |
| rows = 3; |
| } |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| rows, |
| fCameraProfile.fColorPlanes, |
| fAsShotPreProfileMatrix)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AsShotPreProfileMatrix:\n"); |
| |
| DumpMatrix (fAsShotPreProfileMatrix); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCurrentICCProfile: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttUndefined); |
| |
| fCurrentICCProfileCount = tagCount; |
| fCurrentICCProfileOffset = tagOffset; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CurrentICCProfile: Count = %u, Offset = %u\n", |
| (unsigned) fCurrentICCProfileCount, |
| (unsigned) fCurrentICCProfileOffset); |
| |
| DumpHexAscii (stream, tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcCurrentPreProfileMatrix: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttSRational); |
| |
| if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) |
| return false; |
| |
| uint32 rows = fCameraProfile.fColorPlanes; |
| |
| if (tagCount == fCameraProfile.fColorPlanes * 3) |
| { |
| rows = 3; |
| } |
| |
| if (!ParseMatrixTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| rows, |
| fCameraProfile.fColorPlanes, |
| fCurrentPreProfileMatrix)) |
| return false; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("CurrentPreProfileMatrix:\n"); |
| |
| DumpMatrix (fCurrentPreProfileMatrix); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcColorimetricReference: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fColorimetricReference = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ColorimetricReference: %s\n", |
| LookupColorimetricReference (fColorimetricReference)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcExtraCameraProfiles: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttLong8); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1, tagCount); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ExtraCameraProfiles: %u\n", (unsigned) tagCount); |
| |
| } |
| |
| #endif |
| |
| fExtraCameraProfiles.reserve (tagCount); |
| |
| for (uint32 index = 0; index < tagCount; index++) |
| { |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("\nExtraCameraProfile [%u]:\n\n", (unsigned) index); |
| |
| } |
| |
| #endif |
| |
| uint64 profileOffset = stream.TagValue_uint64 (tagType); |
| |
| uint64 savePosition = stream.Position (); |
| |
| dng_camera_profile_info profileInfo; |
| |
| stream.SetReadPosition (profileOffset); |
| |
| if (profileInfo.ParseExtended (stream)) |
| { |
| |
| fExtraCameraProfiles.push_back (profileInfo); |
| |
| } |
| |
| else |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Unable to parse extra camera profile"); |
| |
| #endif |
| |
| } |
| |
| stream.SetReadPosition (savePosition); |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("\nDone with ExtraCameraProfiles\n\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcAsShotProfileName: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); |
| |
| ParseStringTag (stream, |
| parentCode, |
| tagCode, |
| tagCount, |
| fAsShotProfileName, |
| false); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("AsShotProfileName: "); |
| |
| DumpString (fAsShotProfileName); |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalDefaultFinalSize: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) |
| return false; |
| |
| fOriginalDefaultFinalSize.h = stream.TagValue_int32 (tagType); |
| fOriginalDefaultFinalSize.v = stream.TagValue_int32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalDefaultFinalSize: H = %d V = %d\n", |
| (int) fOriginalDefaultFinalSize.h, |
| (int) fOriginalDefaultFinalSize.v); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalBestQualityFinalSize: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) |
| return false; |
| |
| fOriginalBestQualityFinalSize.h = stream.TagValue_int32 (tagType); |
| fOriginalBestQualityFinalSize.v = stream.TagValue_int32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalBestQualityFinalSize: H = %d V = %d\n", |
| (int) fOriginalBestQualityFinalSize.h, |
| (int) fOriginalBestQualityFinalSize.v); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcOriginalDefaultCropSize: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) |
| return false; |
| |
| fOriginalDefaultCropSizeH = stream.TagValue_urational (tagType); |
| fOriginalDefaultCropSizeV = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("OriginalDefaultCropSize: H = %0.2f V = %0.2f\n", |
| fOriginalDefaultCropSizeH.As_real64 (), |
| fOriginalDefaultCropSizeV.As_real64 ()); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDepthFormat: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDepthFormat = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DepthFormat: %s\n", |
| LookupDepthFormat (fDepthFormat)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDepthNear: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDepthNear = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DepthNear: "); |
| |
| if (fDepthNear == dng_urational (0, 0)) |
| { |
| printf ("Unknown"); |
| } |
| else if (fDepthNear.d == 0) |
| { |
| printf ("Infinity"); |
| } |
| else |
| { |
| printf ("%0.2f", fDepthNear.As_real64 ()); |
| } |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDepthFar: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttRational); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDepthFar = stream.TagValue_urational (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DepthFar: "); |
| |
| if (fDepthFar == dng_urational (0, 0)) |
| { |
| printf ("Unknown"); |
| } |
| else if (fDepthFar.d == 0) |
| { |
| printf ("Infinity"); |
| } |
| else |
| { |
| printf ("%0.2f", fDepthFar.As_real64 ()); |
| } |
| |
| printf ("\n"); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDepthUnits: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDepthUnits = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DepthUnits: %s\n", |
| LookupDepthUnits (fDepthUnits)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcDepthMeasureType: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttShort); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fDepthMeasureType = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("DepthMeasureType: %s\n", |
| LookupDepthMeasureType (fDepthMeasureType)); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBigTableDigests: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 16, 0xFFFFFFF0)) |
| return false; |
| |
| uint32 count = tagCount >> 4; |
| |
| fBigTableDigests.clear (); |
| fBigTableDigests.reserve (count); |
| |
| fBigTableOffsets.clear (); |
| fBigTableOffsets.reserve (count); |
| |
| fBigTableByteCounts.clear (); |
| fBigTableByteCounts.reserve (count); |
| |
| for (uint32 index = 0; index < count; index++) |
| { |
| |
| dng_fingerprint fingerprint; |
| |
| stream.Get (fingerprint); |
| |
| fBigTableDigests.push_back (fingerprint); |
| |
| fBigTableOffsets .push_back (0); |
| fBigTableByteCounts.push_back (0); |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BigTableDigests:\n"); |
| |
| for (uint32 index = 0; index < count; index++) |
| { |
| |
| printf ("\t[%u] = ", index); |
| |
| DumpFingerprint (fBigTableDigests [index]); |
| |
| printf ("\n"); |
| |
| } |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBigTableOffsets: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttLong, ttLong8)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, (uint32) fBigTableOffsets.size ())) |
| return false; |
| |
| for (uint32 index = 0; index < tagCount; index++) |
| { |
| |
| fBigTableOffsets [index] = stream.TagValue_uint64 (tagType); |
| |
| if (fBigTableOffsets [index] >= stream.Length ()) |
| { |
| |
| DNG_REPORT ("Invalid big table offset"); |
| |
| fBigTableDigests .clear (); |
| fBigTableOffsets .clear (); |
| fBigTableByteCounts.clear (); |
| |
| return false; |
| |
| } |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| stream.SetReadPosition (tagOffset); |
| |
| DumpTagValues (stream, |
| "Offset", |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBigTableByteCounts: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttLong, ttLong8)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, (uint32) fBigTableByteCounts.size ())) |
| return false; |
| |
| for (uint32 index = 0; index < tagCount; index++) |
| { |
| |
| uint64 byteCount64 = stream.TagValue_uint64 (tagType); |
| |
| if (byteCount64 > 0xFFFFFFFF) |
| { |
| continue; |
| } |
| |
| fBigTableByteCounts [index] = (uint32) byteCount64; |
| |
| if (fBigTableByteCounts [index] + |
| fBigTableOffsets [index] > stream.Length ()) |
| { |
| |
| DNG_REPORT ("Invalid big table byte count"); |
| |
| fBigTableDigests .clear (); |
| fBigTableOffsets .clear (); |
| fBigTableByteCounts.clear (); |
| |
| return false; |
| |
| } |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| stream.SetReadPosition (tagOffset); |
| |
| DumpTagValues (stream, |
| "Count", |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcBigTableGroupIndex: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) |
| return false; |
| |
| if (!CheckTagCount (parentCode, tagCode, tagCount, 32, 0xFFFFFFF0)) |
| return false; |
| |
| // Pairs of digests: |
| // group_digest, dng_fingerprint, 16 bytes |
| // instance_digest, dng_fingerprint, 16 bytes |
| // Each pair is 32 bytes. |
| |
| const uint32 count = tagCount >> 5; |
| |
| fBigTableGroupIndex.clear (); |
| |
| for (uint32 index = 0; index < count; index++) |
| { |
| |
| dng_fingerprint groupDigest; |
| dng_fingerprint instanceDigest; |
| |
| stream.Get (groupDigest); |
| stream.Get (instanceDigest); |
| |
| fBigTableGroupIndex.insert (std::make_pair (groupDigest, |
| instanceDigest)); |
| |
| } |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("BigTableGroupIndex:\n"); |
| |
| uint32 index = 0; |
| |
| for (const auto &entry : fBigTableGroupIndex) |
| { |
| |
| printf ("\t[%u] = ", index); |
| |
| DumpFingerprint (entry.first); |
| |
| printf (" -> "); |
| |
| DumpFingerprint (entry.second); |
| |
| printf ("\n"); |
| |
| index++; |
| |
| } |
| |
| } |
| |
| #endif // qDNGValidate |
| |
| break; |
| |
| } |
| |
| case tcImageSequenceInfo: |
| { |
| |
| if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) |
| return false; |
| |
| constexpr uint32 kMaxTagCount = 1024 * 1024; |
| |
| constexpr uint32 kMinTagCount = (9 + // Sequence ID |
| 2 + // Sequence Type |
| 1 + // Frame Info |
| 4 + // Index |
| 4 + // Count |
| 1); // Final |
| |
| if (tagCount < kMinTagCount) |
| { |
| |
| #if qDNGValidate |
| ReportWarning ("ImageSequenceInfo tag too small -- skipping"); |
| #endif |
| |
| return false; |
| |
| } |
| |
| if (tagCount > kMaxTagCount) |
| { |
| |
| #if qDNGValidate |
| ReportWarning ("ImageSequenceInfo tag unusually large -- skipping"); |
| #endif |
| |
| return false; |
| |
| } |
| |
| dng_image_sequence_info &info = fImageSequenceInfo; |
| |
| std::vector<char> buf; |
| |
| buf.resize (tagCount + 1); |
| |
| char *ptr = &buf [0]; |
| |
| // Read sequence ID. |
| |
| stream.Get_CString (ptr, tagCount); |
| |
| info.fSequenceID.Set (ptr); |
| |
| // Read sequence type. |
| |
| stream.Get_CString (ptr, tagCount); |
| |
| info.fSequenceType.Set (ptr); |
| |
| // Read frame info. |
| |
| stream.Get_CString (ptr, tagCount); |
| |
| info.fFrameInfo.Set (ptr); |
| |
| // Get index, count, and final fields. |
| |
| TempBigEndian tempEndian (stream); |
| |
| info.fIndex = stream.Get_uint32 (); |
| info.fCount = stream.Get_uint32 (); |
| |
| info.fIsFinal = stream.Get_uint8 (); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("ImageSequenceInfo: seq_id=%s, seq_type=%s, " |
| "frame_info=%s, index=%u, count=%u, final=%u\n", |
| info.fSequenceID.Get (), |
| info.fSequenceType.Get (), |
| info.fFrameInfo.Get (), |
| unsigned (info.fIndex), |
| unsigned (info.fCount), |
| unsigned (info.fIsFinal)); |
| |
| |
| } |
| |
| #endif // qDNGValidate |
| |
| break; |
| |
| } |
| |
| default: |
| { |
| |
| // The main camera profile tags also appear in IFD 0 |
| |
| return fCameraProfile.ParseTag (stream, |
| parentCode, |
| tagCode, |
| tagType, |
| tagCount, |
| tagOffset); |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| // Parses tags that should only appear in IFD 0 or EXIF IFD. |
| |
| bool dng_shared::Parse_ifd0_exif (dng_stream &stream, |
| dng_exif & /* exif */, |
| uint32 parentCode, |
| uint32 tagCode, |
| uint32 tagType, |
| uint32 tagCount, |
| uint64 tagOffset) |
| { |
| |
| switch (tagCode) |
| { |
| |
| case tcMakerNote: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttUndefined); |
| |
| fMakerNoteCount = tagCount; |
| fMakerNoteOffset = tagOffset; |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| |
| printf ("MakerNote: Count = %u, Offset = %u\n", |
| (unsigned) fMakerNoteCount, |
| (unsigned) fMakerNoteOffset); |
| |
| DumpHexAscii (stream, tagCount); |
| |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| case tcInteroperabilityIFD: |
| { |
| |
| CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); |
| |
| CheckTagCount (parentCode, tagCode, tagCount, 1); |
| |
| fInteroperabilityIFD = stream.TagValue_uint32 (tagType); |
| |
| #if qDNGValidate |
| |
| if (gVerbose) |
| { |
| printf ("InteroperabilityIFD: %u\n", (unsigned) fInteroperabilityIFD); |
| } |
| |
| #endif |
| |
| break; |
| |
| } |
| |
| default: |
| { |
| |
| return false; |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_shared::PostParse (dng_host & /* host */, |
| dng_exif & /* exif */) |
| { |
| |
| // Fill in default values for DNG images. |
| |
| if (fDNGVersion != 0) |
| { |
| |
| // Support for DNG versions before 1.0.0.0. |
| |
| if (fDNGVersion < dngVersion_1_0_0_0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("DNGVersion less than 1.0.0.0"); |
| |
| #endif |
| |
| // The CalibrationIlluminant tags were added just before |
| // DNG version 1.0.0.0, and were hardcoded before that. |
| |
| fCameraProfile.fCalibrationIlluminant1 = lsStandardLightA; |
| fCameraProfile.fCalibrationIlluminant2 = lsD65; |
| fCameraProfile.fCalibrationIlluminant3 = lsD65; |
| |
| fDNGVersion = dngVersion_1_0_0_0; |
| |
| } |
| |
| // Default value for DNGBackwardVersion tag. |
| |
| if (fDNGBackwardVersion == 0) |
| { |
| |
| fDNGBackwardVersion = fDNGVersion & 0xFFFF0000; |
| |
| } |
| |
| // Check DNGBackwardVersion value. |
| |
| if (fDNGBackwardVersion < dngVersion_1_0_0_0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("DNGBackwardVersion less than 1.0.0.0"); |
| |
| #endif |
| |
| fDNGBackwardVersion = dngVersion_1_0_0_0; |
| |
| } |
| |
| if (fDNGBackwardVersion > fDNGVersion) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("DNGBackwardVersion > DNGVersion"); |
| |
| #endif |
| |
| fDNGBackwardVersion = fDNGVersion; |
| |
| } |
| |
| // Check UniqueCameraModel. |
| |
| if (fUniqueCameraModel.IsEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Missing or invalid UniqueCameraModel"); |
| |
| #endif |
| |
| fUniqueCameraModel.Set ("Digital Negative"); |
| |
| } |
| |
| // If we don't know the color depth yet, it must be a monochrome DNG. |
| |
| if (fCameraProfile.fColorPlanes == 0) |
| { |
| |
| fCameraProfile.fColorPlanes = 1; |
| |
| } |
| |
| if ((fCameraProfile.fCalibrationIlluminant3 != lsUnknown) && |
| fCameraProfile.fColorMatrix3.IsEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Ignoring CalibrationIlluminant3 because " |
| "ColorMatrix3 invalid or missing"); |
| |
| #endif |
| |
| fCameraProfile.fCalibrationIlluminant3 = lsUnknown; |
| |
| } |
| |
| if (fCameraProfile.fCalibrationIlluminant3 == lsUnknown) |
| { |
| |
| if (fCameraProfile.fColorMatrix3.NotEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Ignoring ColorMatrix3 because " |
| "CalibrationIlluminant3 is unknown/missing"); |
| |
| #endif |
| |
| fCameraProfile.fColorMatrix3.Clear (); |
| |
| } |
| |
| if (fCameraProfile.fForwardMatrix3.NotEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Ignoring ForwardMatrix3 because " |
| "CalibrationIlluminant3 is unknown/missing"); |
| |
| #endif |
| |
| fCameraProfile.fForwardMatrix3.Clear (); |
| |
| } |
| |
| if (fCameraProfile.fReductionMatrix3.NotEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Ignoring ReductionMatrix3 because " |
| "CalibrationIlluminant3 is unknown/missing"); |
| |
| #endif |
| |
| fCameraProfile.fReductionMatrix3.Clear (); |
| |
| } |
| |
| if (fCameraProfile.fHueSatDeltas3Offset != 0 || |
| fCameraProfile.fHueSatDeltas3Count != 0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Ignoring ProfileHueSatMapData3 because " |
| "CalibrationIlluminant3 is unknown/missing"); |
| |
| #endif |
| |
| fCameraProfile.fHueSatDeltas3Offset = 0; |
| fCameraProfile.fHueSatDeltas3Count = 0; |
| |
| } |
| |
| } |
| |
| // Check color info. |
| |
| if (fCameraProfile.fColorPlanes > 1) |
| { |
| |
| // Check illuminant pair. This is for the dual-illuminant model. |
| |
| // More detailed checks for triple-illuminant profiles are done in |
| // dng_camera_profile::IsValid. |
| |
| if (fCameraProfile.fColorMatrix2.NotEmpty ()) |
| { |
| |
| // Are illuminants 1 and 2 the same? Start by assuming not. |
| |
| bool sameLight = false; |
| |
| if (fCameraProfile.fCalibrationIlluminant1 == fCameraProfile.fCalibrationIlluminant2) |
| { |
| |
| // Lights 1 and 2 have the same tag code. If they are |
| // something besides lsOther, then they're the same light. |
| |
| if (fCameraProfile.fCalibrationIlluminant1 != lsOther) |
| { |
| |
| sameLight = true; |
| |
| } |
| |
| // Both are lsOther, so check the custom data fields for |
| // the same white point. |
| |
| else |
| { |
| |
| dng_xy_coord white1 = fCameraProfile.fIlluminantData1.WhiteXY (); |
| dng_xy_coord white2 = fCameraProfile.fIlluminantData2.WhiteXY (); |
| |
| sameLight = (white1 == white2); |
| |
| } |
| |
| } |
| |
| if (fCameraProfile.fCalibrationIlluminant1 == lsUnknown || |
| fCameraProfile.fCalibrationIlluminant2 == lsUnknown || |
| sameLight) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Invalid CalibrationIlluminant pair"); |
| |
| #endif |
| |
| fCameraProfile.fColorMatrix2 = dng_matrix (); |
| |
| } |
| |
| } |
| |
| // If the colorimetric reference is the ICC profile PCS, then the |
| // data must already be white balanced. The "AsShotWhiteXY" is |
| // required to be the ICC Profile PCS white point. |
| |
| if (fColorimetricReference == crICCProfilePCS || |
| fColorimetricReference == crOutputReferredHDR) |
| { |
| |
| if (fAsShotNeutral.NotEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("AsShotNeutral not allowed for this " |
| "ColorimetricReference value"); |
| |
| #endif |
| |
| fAsShotNeutral.Clear (); |
| |
| } |
| |
| dng_xy_coord pcs = PCStoXY (); |
| |
| #if qDNGValidate |
| |
| if (fAsShotWhiteXY.IsValid ()) |
| { |
| |
| if (Abs_real64 (fAsShotWhiteXY.x - pcs.x) > 0.01 || |
| Abs_real64 (fAsShotWhiteXY.y - pcs.y) > 0.01) |
| { |
| |
| ReportWarning ("AsShotWhiteXY does not match the ICC Profile PCS"); |
| |
| } |
| |
| } |
| |
| #endif |
| |
| fAsShotWhiteXY = pcs; |
| |
| } |
| |
| else |
| { |
| |
| // Warn if both AsShotNeutral and AsShotWhiteXY are specified. |
| |
| if (fAsShotNeutral.NotEmpty () && fAsShotWhiteXY.IsValid ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Both AsShotNeutral and AsShotWhiteXY included"); |
| |
| #endif |
| |
| fAsShotWhiteXY = dng_xy_coord (); |
| |
| } |
| |
| // Warn if neither AsShotNeutral nor AsShotWhiteXY are specified. |
| |
| #if qDNGValidate |
| |
| if (fAsShotNeutral.IsEmpty () && !fAsShotWhiteXY.IsValid ()) |
| { |
| |
| ReportWarning ("Neither AsShotNeutral nor AsShotWhiteXY included", |
| "legal but not recommended"); |
| |
| } |
| |
| #endif |
| |
| } |
| |
| // Default values of calibration signatures are required for legacy |
| // compatibility. |
| |
| if (fCameraProfile.fCalibrationIlluminant1 == lsStandardLightA && |
| fCameraProfile.fCalibrationIlluminant2 == lsD65 && |
| fCameraProfile.fCalibrationIlluminant3 == lsUnknown && |
| fCameraCalibration1.Rows () == fCameraProfile.fColorPlanes && |
| fCameraCalibration1.Cols () == fCameraProfile.fColorPlanes && |
| fCameraCalibration2.Rows () == fCameraProfile.fColorPlanes && |
| fCameraCalibration2.Cols () == fCameraProfile.fColorPlanes && |
| fCameraCalibration3.IsEmpty () && |
| fCameraCalibrationSignature.IsEmpty () && |
| fCameraProfile.fProfileCalibrationSignature.IsEmpty () ) |
| { |
| |
| fCameraCalibrationSignature.Set (kAdobeCalibrationSignature); |
| |
| fCameraProfile.fProfileCalibrationSignature.Set (kAdobeCalibrationSignature); |
| |
| } |
| |
| } |
| |
| // Check BaselineNoise. |
| |
| if (fBaselineNoise.As_real64 () <= 0.0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Invalid BaselineNoise"); |
| |
| #endif |
| |
| fBaselineNoise = dng_urational (1, 1); |
| |
| } |
| |
| // Check BaselineSharpness. |
| |
| if (fBaselineSharpness.As_real64 () <= 0.0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Invalid BaselineSharpness"); |
| |
| #endif |
| |
| fBaselineSharpness = dng_urational (1, 1); |
| |
| } |
| |
| // Check LinearResponseLimit. |
| |
| if (fLinearResponseLimit.As_real64 () < 0.5 || |
| fLinearResponseLimit.As_real64 () > 1.0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Invalid LinearResponseLimit"); |
| |
| #endif |
| |
| fLinearResponseLimit = dng_urational (1, 1); |
| |
| } |
| |
| // Check ShadowScale. |
| |
| if (fShadowScale.As_real64 () <= 0.0) |
| { |
| |
| #if qDNGValidate |
| |
| ReportWarning ("Invalid ShadowScale"); |
| |
| #endif |
| |
| fShadowScale = dng_urational (1, 1); |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_shared::IsValidDNG () |
| { |
| |
| // Check DNGVersion value. |
| |
| if (fDNGVersion < dngVersion_1_0_0_0) |
| { |
| |
| #if qDNGValidate |
| |
| if (fDNGVersion != dngVersion_None) |
| { |
| |
| ReportError ("Invalid DNGVersion"); |
| |
| } |
| |
| #if qDNGValidateTarget |
| |
| else |
| { |
| |
| ReportError ("Missing DNGVersion"); |
| |
| } |
| |
| #endif |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Check DNGBackwardVersion value. |
| |
| if (fDNGBackwardVersion > dngVersion_Current) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("DNGBackwardVersion (or DNGVersion) is too high"); |
| |
| #endif |
| |
| ThrowUnsupportedDNG (); |
| |
| } |
| |
| // Check color transform info. |
| |
| if (fCameraProfile.fColorPlanes > 1) |
| { |
| |
| // CameraCalibration1 is optional, but it must be valid if present. |
| |
| if (fCameraCalibration1.Cols () != 0 || |
| fCameraCalibration1.Rows () != 0) |
| { |
| |
| if (fCameraCalibration1.Cols () != fCameraProfile.fColorPlanes || |
| fCameraCalibration1.Rows () != fCameraProfile.fColorPlanes) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration1 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Make sure it is invertable. |
| |
| try |
| { |
| |
| (void) Invert (fCameraCalibration1); |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration1 is not invertable"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // CameraCalibration2 is optional, but it must be valid if present. |
| |
| if (fCameraCalibration2.Cols () != 0 || |
| fCameraCalibration2.Rows () != 0) |
| { |
| |
| if (fCameraCalibration2.Cols () != fCameraProfile.fColorPlanes || |
| fCameraCalibration2.Rows () != fCameraProfile.fColorPlanes) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration2 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Make sure it is invertable. |
| |
| try |
| { |
| |
| (void) Invert (fCameraCalibration2); |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration2 is not invertable"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // CameraCalibration3 is optional, but it must be valid if present. |
| |
| if (fCameraCalibration3.Cols () != 0 || |
| fCameraCalibration3.Rows () != 0) |
| { |
| |
| if (fCameraCalibration3.Cols () != fCameraProfile.fColorPlanes || |
| fCameraCalibration3.Rows () != fCameraProfile.fColorPlanes) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration3 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Make sure it is invertable. |
| |
| try |
| { |
| |
| (void) Invert (fCameraCalibration3); |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CameraCalibration3 is not invertable"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // Check analog balance |
| |
| dng_matrix analogBalance; |
| |
| if (fAnalogBalance.NotEmpty ()) |
| { |
| |
| analogBalance = fAnalogBalance.AsDiagonal (); |
| |
| try |
| { |
| |
| (void) Invert (analogBalance); |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("AnalogBalance is not invertable"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |