You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3297 lines
56 KiB
3297 lines
56 KiB
/*****************************************************************************/ |
|
// Copyright 2006-2008 Adobe Systems Incorporated |
|
// All Rights Reserved. |
|
// |
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in |
|
// accordance with the terms of the Adobe license agreement accompanying it. |
|
/*****************************************************************************/ |
|
|
|
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_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; |
|
|
|
} |
|
|
|
/*****************************************************************************/
|
|
|