blob: 4b00d242e6f01a3ed82e6247f4573bdb8044211f [file] [log] [blame]
/*****************************************************************************/
// 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<dng_noise_function> 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;
}
/*****************************************************************************/