/*****************************************************************************/ // 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_shared.cpp#2 $ */ /* $DateTime: 2012/06/14 20:24:41 $ */ /* $Change: 835078 $ */ /* $Author: tknoll $ */ /*****************************************************************************/ #include "dng_shared.h" #include "dng_camera_profile.h" #include "dng_exceptions.h" #include "dng_globals.h" #include "dng_memory.h" #include "dng_parse_utils.h" #include "dng_safe_arithmetic.h" #include "dng_tag_codes.h" #include "dng_tag_types.h" #include "dng_tag_values.h" #include "dng_utils.h" /*****************************************************************************/ dng_camera_profile_info::dng_camera_profile_info () : fBigEndian (false) , fColorPlanes (0) , fCalibrationIlluminant1 (lsUnknown) , fCalibrationIlluminant2 (lsUnknown) , fColorMatrix1 () , fColorMatrix2 () , fForwardMatrix1 () , fForwardMatrix2 () , fReductionMatrix1 () , fReductionMatrix2 () , fProfileCalibrationSignature () , fProfileName () , fProfileCopyright () , fEmbedPolicy (pepAllowCopying) , fProfileHues (0) , fProfileSats (0) , fProfileVals (0) , fHueSatDeltas1Offset (0) , fHueSatDeltas1Count (0) , fHueSatDeltas2Offset (0) , fHueSatDeltas2Count (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) , fUniqueCameraModel () { } /*****************************************************************************/ dng_camera_profile_info::~dng_camera_profile_info () { } /*****************************************************************************/ 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 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 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 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 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; bool skipSat0 = (tagCount == SafeUint32Mult(fProfileHues, SafeUint32Sub(fProfileSats, 1u), fProfileVals, 3u)); if (!skipSat0) { if (!CheckTagCount (parentCode, tagCode, tagCount, SafeUint32Mult(fProfileHues, fProfileSats, fProfileVals, 3))) 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; bool skipSat0 = (tagCount == SafeUint32Mult(fProfileHues, SafeUint32Sub(fProfileSats, 1u), fProfileVals, 3u)); if (!skipSat0) { if (!CheckTagCount (parentCode, tagCode, tagCount, SafeUint32Mult(fProfileHues, fProfileSats, fProfileVals, 3))) 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 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; bool skipSat0 = (tagCount == SafeUint32Mult(fLookTableHues, SafeUint32Sub(fLookTableSats, 1u), fLookTableVals, 3u)); if (!skipSat0) { if (!CheckTagCount (parentCode, tagCode, tagCount, SafeUint32Mult(fLookTableHues, fLookTableSats, fLookTableVals, 3))) 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]; sprintf (message, "%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 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; } 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 () , fCameraCalibrationSignature () , fAnalogBalance () , fAsShotNeutral () , fAsShotWhiteXY () , fBaselineExposure (0, 1) , fBaselineNoise (1, 1) , fNoiseReductionApplied (0, 0) , 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 () , fNoiseProfile () , fOriginalDefaultFinalSize () , fOriginalBestQualityFinalSize () , fOriginalDefaultCropSizeH () , fOriginalDefaultCropSizeV () { } /*****************************************************************************/ 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 = SafeUint32Mult(tagCount, TagTypeSize(tagType)); fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0; #if qDNGValidate if (gVerbose) { printf ("IPTC/NAA: Count = %u, Offset = %u\n", (unsigned) fIPTC_NAA_Count, (unsigned) 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_printer printer; printer.Process (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_printer printer; printer.Process (data, count); printf ("IPTCDigest (ignoring zero padding): "); DumpFingerprint (printer.Result ()); printf ("\n"); } } } #endif break; } case tcExifIFD: { CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); CheckTagCount (parentCode, tagCode, tagCount, 1); fExifIFD = stream.TagValue_uint32 (tagType); #if qDNGValidate if (gVerbose) { printf ("ExifIFD: %u\n", (unsigned) fExifIFD); } #endif break; } case tcGPSInfo: { CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); CheckTagCount (parentCode, tagCode, tagCount, 1); fGPSInfo = stream.TagValue_uint32 (tagType); #if qDNGValidate if (gVerbose) { printf ("GPSInfo: %u\n", (unsigned) 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 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 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 tcNoiseReductionApplied: { if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) return false; if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) return false; fNoiseReductionApplied = stream.TagValue_urational (tagType); #if qDNGValidate if (gVerbose) { printf ("NoiseReductionApplied: %u/%u\n", (unsigned) fNoiseReductionApplied.n, (unsigned) fNoiseReductionApplied.d); } #endif break; } case tcNoiseProfile: { if (!CheckTagType (parentCode, tagCode, tagType, ttDouble)) return false; // Must be an even, positive number of doubles in a noise profile. if (!tagCount || (tagCount & 1)) return false; // Determine number of planes (i.e., half the number of doubles). const uint32 numPlanes = Pin_uint32 (0, tagCount >> 1, kMaxColorPlanes); // Parse the noise function parameters. dng_std_vector noiseFunctions; for (uint32 i = 0; i < numPlanes; i++) { const real64 scale = stream.TagValue_real64 (tagType); const real64 offset = stream.TagValue_real64 (tagType); noiseFunctions.push_back (dng_noise_function (scale, offset)); } // Store the noise profile. fNoiseProfile = dng_noise_profile (noiseFunctions); // Debug. #if qDNGValidate if (gVerbose) { printf ("NoiseProfile:\n"); printf (" Planes: %u\n", (unsigned) numPlanes); for (uint32 plane = 0; plane < numPlanes; plane++) { printf (" Noise function for plane %u: scale = %.8lf, offset = %.8lf\n", (unsigned) plane, noiseFunctions [plane].Scale (), noiseFunctions [plane].Offset ()); } } #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.data, 16); #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.data, 16); #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.data, 16); #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.data, 16); #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); 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 stream.SetReadPosition (tagOffset + index * 4); uint32 profileOffset = stream.TagValue_uint32 (tagType); 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 } } #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; } 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; 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; } // Check color info. if (fCameraProfile.fColorPlanes > 1) { // Check illuminant pair. if (fCameraProfile.fColorMatrix2.NotEmpty ()) { if (fCameraProfile.fCalibrationIlluminant1 == lsUnknown || (fCameraProfile.fCalibrationIlluminant2 == lsUnknown || (fCameraProfile.fCalibrationIlluminant1 == fCameraProfile.fCalibrationIlluminant2))) { #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) { 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 // compatiblity. if (fCameraProfile.fCalibrationIlluminant1 == lsStandardLightA && fCameraProfile.fCalibrationIlluminant2 == lsD65 && fCameraCalibration1.Rows () == fCameraProfile.fColorPlanes && fCameraCalibration1.Cols () == fCameraProfile.fColorPlanes && fCameraCalibration2.Rows () == fCameraProfile.fColorPlanes && fCameraCalibration2.Cols () == fCameraProfile.fColorPlanes && 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 NoiseProfile. if (!fNoiseProfile.IsValid () && fNoiseProfile.NumFunctions () != 0) { #if qDNGValidate ReportWarning ("Invalid NoiseProfile"); #endif fNoiseProfile = dng_noise_profile (); } // 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; } } // 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; } /*****************************************************************************/