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.
4268 lines
72 KiB
4268 lines
72 KiB
/*****************************************************************************/
|
|
// Copyright 2006-2012 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_ifd.cpp#3 $ */
|
|
/* $DateTime: 2012/06/05 11:05:39 $ */
|
|
/* $Change: 833352 $ */
|
|
/* $Author: tknoll $ */
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include "dng_ifd.h"
|
|
|
|
#include "dng_exceptions.h"
|
|
#include "dng_flags.h"
|
|
#include "dng_globals.h"
|
|
#include "dng_ifd.h"
|
|
#include "dng_types.h"
|
|
#include "dng_parse_utils.h"
|
|
#include "dng_read_image.h"
|
|
#include "dng_stream.h"
|
|
#include "dng_tag_codes.h"
|
|
#include "dng_tag_types.h"
|
|
#include "dng_tag_values.h"
|
|
#include "dng_utils.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_preview_info::dng_preview_info ()
|
|
|
|
: fIsPrimary (true)
|
|
, fApplicationName ()
|
|
, fApplicationVersion ()
|
|
, fSettingsName ()
|
|
, fSettingsDigest ()
|
|
, fColorSpace (previewColorSpace_MaxEnum)
|
|
, fDateTime ()
|
|
, fRawToPreviewGain (1.0)
|
|
, fCacheVersion (0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_preview_info::~dng_preview_info ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_ifd::dng_ifd ()
|
|
|
|
: fUsesNewSubFileType (false)
|
|
, fNewSubFileType (0)
|
|
|
|
, fImageWidth (0)
|
|
, fImageLength (0)
|
|
|
|
, fCompression (ccUncompressed)
|
|
, fPredictor (cpNullPredictor)
|
|
|
|
, fPhotometricInterpretation (0xFFFFFFFF)
|
|
|
|
, fFillOrder (1)
|
|
|
|
, fOrientation (0)
|
|
, fOrientationType (0)
|
|
, fOrientationOffset (kDNGStreamInvalidOffset)
|
|
, fOrientationBigEndian (false)
|
|
|
|
, fSamplesPerPixel (1)
|
|
|
|
, fPlanarConfiguration (pcInterleaved)
|
|
|
|
, fXResolution (0.0)
|
|
, fYResolution (0.0)
|
|
, fResolutionUnit (0)
|
|
|
|
, fUsesStrips (false)
|
|
, fUsesTiles (false)
|
|
|
|
, fTileWidth (0)
|
|
, fTileLength (0)
|
|
|
|
, fTileOffsetsType (0)
|
|
, fTileOffsetsCount (0)
|
|
, fTileOffsetsOffset (0)
|
|
|
|
, fTileByteCountsType (0)
|
|
, fTileByteCountsCount (0)
|
|
, fTileByteCountsOffset (0)
|
|
|
|
, fSubIFDsCount (0)
|
|
, fSubIFDsOffset (0)
|
|
|
|
, fExtraSamplesCount (0)
|
|
|
|
, fJPEGTablesCount (0)
|
|
, fJPEGTablesOffset (0)
|
|
|
|
, fJPEGInterchangeFormat (0)
|
|
, fJPEGInterchangeFormatLength (0)
|
|
|
|
, fYCbCrCoefficientR (0.0)
|
|
, fYCbCrCoefficientG (0.0)
|
|
, fYCbCrCoefficientB (0.0)
|
|
|
|
, fYCbCrSubSampleH (0)
|
|
, fYCbCrSubSampleV (0)
|
|
|
|
, fYCbCrPositioning (0)
|
|
|
|
, fCFARepeatPatternRows (0)
|
|
, fCFARepeatPatternCols (0)
|
|
|
|
, fCFALayout (1)
|
|
|
|
, fLinearizationTableType (0)
|
|
, fLinearizationTableCount (0)
|
|
, fLinearizationTableOffset (0)
|
|
|
|
, fBlackLevelRepeatRows (1)
|
|
, fBlackLevelRepeatCols (1)
|
|
|
|
, fBlackLevelDeltaHType (0)
|
|
, fBlackLevelDeltaHCount (0)
|
|
, fBlackLevelDeltaHOffset (0)
|
|
|
|
, fBlackLevelDeltaVType (0)
|
|
, fBlackLevelDeltaVCount (0)
|
|
, fBlackLevelDeltaVOffset (0)
|
|
|
|
, fDefaultScaleH (1, 1)
|
|
, fDefaultScaleV (1, 1)
|
|
|
|
, fBestQualityScale (1, 1)
|
|
|
|
, fDefaultCropOriginH (0, 1)
|
|
, fDefaultCropOriginV (0, 1)
|
|
|
|
, fDefaultCropSizeH ()
|
|
, fDefaultCropSizeV ()
|
|
|
|
, fDefaultUserCropT (0, 1)
|
|
, fDefaultUserCropL (0, 1)
|
|
, fDefaultUserCropB (1, 1)
|
|
, fDefaultUserCropR (1, 1)
|
|
|
|
, fBayerGreenSplit (0)
|
|
|
|
, fChromaBlurRadius ()
|
|
|
|
, fAntiAliasStrength (1, 1)
|
|
|
|
, fActiveArea ()
|
|
|
|
, fMaskedAreaCount (0)
|
|
|
|
, fRowInterleaveFactor (1)
|
|
|
|
, fSubTileBlockRows (1)
|
|
, fSubTileBlockCols (1)
|
|
|
|
, fPreviewInfo ()
|
|
|
|
, fOpcodeList1Count (0)
|
|
, fOpcodeList1Offset (0)
|
|
|
|
, fOpcodeList2Count (0)
|
|
, fOpcodeList2Offset (0)
|
|
|
|
, fOpcodeList3Count (0)
|
|
, fOpcodeList3Offset (0)
|
|
|
|
, fLosslessJPEGBug16 (false)
|
|
|
|
, fSampleBitShift (0)
|
|
|
|
, fThisIFD (0)
|
|
, fNextIFD (0)
|
|
|
|
, fCompressionQuality (-1)
|
|
|
|
, fPatchFirstJPEGByte (false)
|
|
|
|
{
|
|
|
|
uint32 j;
|
|
uint32 k;
|
|
uint32 n;
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
fBitsPerSample [j] = 0;
|
|
}
|
|
|
|
for (j = 0; j < kMaxTileInfo; j++)
|
|
{
|
|
fTileOffset [j] = 0;
|
|
fTileByteCount [j] = 0;
|
|
}
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
fExtraSamples [j] = esUnspecified;
|
|
}
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
fSampleFormat [j] = sfUnsignedInteger;
|
|
}
|
|
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
fReferenceBlackWhite [j] = 0.0;
|
|
}
|
|
|
|
for (j = 0; j < kMaxCFAPattern; j++)
|
|
for (k = 0; k < kMaxCFAPattern; k++)
|
|
{
|
|
fCFAPattern [j] [k] = 255;
|
|
}
|
|
|
|
for (j = 0; j < kMaxColorPlanes; j++)
|
|
{
|
|
fCFAPlaneColor [j] = (uint8) (j < 3 ? j : 255);
|
|
}
|
|
|
|
for (j = 0; j < kMaxBlackPattern; j++)
|
|
for (k = 0; k < kMaxBlackPattern; k++)
|
|
for (n = 0; n < kMaxSamplesPerPixel; n++)
|
|
{
|
|
fBlackLevel [j] [k] [n] = 0.0;
|
|
}
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
fWhiteLevel [j] = -1.0; // Don't know real default yet.
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_ifd::~dng_ifd ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
// Parses tags that should only appear in IFDs that contain images.
|
|
|
|
bool dng_ifd::ParseTag (dng_stream &stream,
|
|
uint32 parentCode,
|
|
uint32 tagCode,
|
|
uint32 tagType,
|
|
uint32 tagCount,
|
|
uint64 tagOffset)
|
|
{
|
|
|
|
uint32 j;
|
|
uint32 k;
|
|
uint32 n;
|
|
|
|
switch (tagCode)
|
|
{
|
|
|
|
case tcNewSubFileType:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fUsesNewSubFileType = true;
|
|
|
|
fNewSubFileType = stream.TagValue_uint32 (tagType);
|
|
|
|
fPreviewInfo.fIsPrimary = (fNewSubFileType == sfPreviewImage);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("NewSubFileType: %s\n",
|
|
LookupNewSubFileType (fNewSubFileType));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcImageWidth:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fImageWidth = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("ImageWidth: %u\n", (unsigned) fImageWidth);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcImageLength:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fImageLength = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("ImageLength: %u\n", (unsigned) fImageLength);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBitsPerSample:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1, 0x0FFFF);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("BitsPerSample:");
|
|
}
|
|
|
|
#endif
|
|
|
|
bool extrasMatch = true;
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
uint32 x = stream.TagValue_uint32 (tagType);
|
|
|
|
const uint32 maxBitsPerSample = 32;
|
|
|
|
if (j < kMaxSamplesPerPixel)
|
|
{
|
|
|
|
if (x > maxBitsPerSample)
|
|
{
|
|
ThrowBadFormat ("BitsPerSample out of bounds.");
|
|
}
|
|
|
|
fBitsPerSample [j] = x;
|
|
}
|
|
|
|
else if (x != fBitsPerSample [kMaxSamplesPerPixel - 1])
|
|
{
|
|
extrasMatch = false;
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf (" %u", (unsigned) x);
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!extrasMatch)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("BitsPerSample not constant");
|
|
|
|
#endif
|
|
|
|
ThrowBadFormat ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCompression:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fCompression = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("Compression: %s\n",
|
|
LookupCompression (fCompression));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// Correct a common TIFF writer mistake.
|
|
|
|
if (fCompression == 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
{
|
|
|
|
char message [256];
|
|
|
|
sprintf (message,
|
|
"%s has invalid zero compression code",
|
|
LookupParentCode (parentCode));
|
|
|
|
ReportWarning (message);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
fCompression = ccUncompressed;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPhotometricInterpretation:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fPhotometricInterpretation = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PhotometricInterpretation: %s\n",
|
|
LookupPhotometricInterpretation (fPhotometricInterpretation));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcFillOrder:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fFillOrder = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("FillOrder: %u\n", (unsigned) fFillOrder);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcStripOffsets:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
fUsesStrips = true;
|
|
|
|
fTileOffsetsType = tagType;
|
|
fTileOffsetsCount = tagCount;
|
|
fTileOffsetsOffset = tagOffset;
|
|
|
|
if (tagCount <= kMaxTileInfo)
|
|
{
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
fTileOffset [j] = stream.TagValue_uint32 (tagType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
stream.SetReadPosition (tagOffset);
|
|
|
|
DumpTagValues (stream,
|
|
"Offset",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcOrientation:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fOrientationType = tagType;
|
|
fOrientationOffset = stream.PositionInOriginalFile ();
|
|
fOrientationBigEndian = stream.BigEndian ();
|
|
|
|
fOrientation = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("Orientation: %s\n",
|
|
LookupOrientation (fOrientation));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcSamplesPerPixel:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fSamplesPerPixel = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("SamplesPerPixel: %u\n", (unsigned) fSamplesPerPixel);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcRowsPerStrip:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fUsesStrips = true;
|
|
|
|
fTileLength = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("RowsPerStrip: %u\n", (unsigned) fTileLength);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcStripByteCounts:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
fUsesStrips = true;
|
|
|
|
fTileByteCountsType = tagType;
|
|
fTileByteCountsCount = tagCount;
|
|
fTileByteCountsOffset = tagOffset;
|
|
|
|
if (tagCount <= kMaxTileInfo)
|
|
{
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
fTileByteCount [j] = stream.TagValue_uint32 (tagType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
stream.SetReadPosition (tagOffset);
|
|
|
|
DumpTagValues (stream,
|
|
"Count",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcXResolution:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fXResolution = stream.TagValue_real64 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("XResolution: %0.2f\n", fXResolution);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcYResolution:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fYResolution = stream.TagValue_real64 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("YResolution: %0.2f\n", fYResolution);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPlanarConfiguration:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fPlanarConfiguration = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("PlanarConfiguration: %u\n", (unsigned) fPlanarConfiguration);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcResolutionUnit:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fResolutionUnit = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("ResolutionUnit: %s\n",
|
|
LookupResolutionUnit (fResolutionUnit));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPredictor:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fPredictor = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("Predictor: %s\n",
|
|
LookupPredictor (fPredictor));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcTileWidth:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fUsesTiles = true;
|
|
|
|
fTileWidth = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("TileWidth: %u\n", (unsigned) fTileWidth);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcTileLength:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fUsesTiles = true;
|
|
|
|
fTileLength = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("TileLength: %u\n", (unsigned) fTileLength);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcTileOffsets:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
fUsesTiles = true;
|
|
|
|
fTileOffsetsType = tagType;
|
|
fTileOffsetsCount = tagCount;
|
|
fTileOffsetsOffset = tagOffset;
|
|
|
|
if (tagCount <= kMaxTileInfo)
|
|
{
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
fTileOffset [j] = stream.TagValue_uint32 (tagType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
stream.SetReadPosition (tagOffset);
|
|
|
|
DumpTagValues (stream,
|
|
"Offset",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcTileByteCounts:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
fUsesTiles = true;
|
|
|
|
fTileByteCountsType = tagType;
|
|
fTileByteCountsCount = tagCount;
|
|
fTileByteCountsOffset = tagOffset;
|
|
|
|
if (tagCount <= kMaxTileInfo)
|
|
{
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
fTileByteCount [j] = stream.TagValue_uint32 (tagType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
stream.SetReadPosition (tagOffset);
|
|
|
|
DumpTagValues (stream,
|
|
"Count",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcSubIFDs:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD);
|
|
|
|
fSubIFDsCount = tagCount;
|
|
fSubIFDsOffset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
DumpTagValues (stream,
|
|
"IFD",
|
|
parentCode,
|
|
tagCode,
|
|
ttLong,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcExtraSamples:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1, fSamplesPerPixel);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("ExtraSamples:");
|
|
}
|
|
|
|
#endif
|
|
|
|
fExtraSamplesCount = tagCount;
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
uint32 x = stream.TagValue_uint32 (tagType);
|
|
|
|
if (j < kMaxSamplesPerPixel)
|
|
{
|
|
fExtraSamples [j] = x;
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf (" %u", (unsigned) x);
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcSampleFormat:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("SampleFormat:");
|
|
}
|
|
|
|
#endif
|
|
|
|
bool extrasMatch = true;
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
uint32 x = stream.TagValue_uint32 (tagType);
|
|
|
|
if (j < kMaxSamplesPerPixel)
|
|
{
|
|
fSampleFormat [j] = x;
|
|
}
|
|
|
|
else if (x != fSampleFormat [kMaxSamplesPerPixel - 1])
|
|
{
|
|
extrasMatch = false;
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf (" %s", LookupSampleFormat (x));
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!extrasMatch)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("SampleFormat not constant");
|
|
|
|
#endif
|
|
|
|
ThrowBadFormat ();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcJPEGTables:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
|
|
|
|
fJPEGTablesCount = tagCount;
|
|
fJPEGTablesOffset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("JPEGTables: count = %u, offset = %u\n",
|
|
(unsigned) fJPEGTablesCount,
|
|
(unsigned) fJPEGTablesOffset);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcJPEGInterchangeFormat:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fJPEGInterchangeFormat = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("JPEGInterchangeFormat: %u\n",
|
|
(unsigned) fJPEGInterchangeFormat);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcJPEGInterchangeFormatLength:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fJPEGInterchangeFormatLength = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("JPEGInterchangeFormatLength: %u\n",
|
|
(unsigned) fJPEGInterchangeFormatLength);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcYCbCrCoefficients:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 3))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
fYCbCrCoefficientR = stream.TagValue_real64 (tagType);
|
|
fYCbCrCoefficientG = stream.TagValue_real64 (tagType);
|
|
fYCbCrCoefficientB = stream.TagValue_real64 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("YCbCrCoefficients: R = %0.3f, G = %0.3f, B = %0.3f\n",
|
|
fYCbCrCoefficientR,
|
|
fYCbCrCoefficientG,
|
|
fYCbCrCoefficientB);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcYCbCrSubSampling:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
fYCbCrSubSampleH = stream.TagValue_uint32 (tagType);
|
|
fYCbCrSubSampleV = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("YCbCrSubSampling: H = %u, V = %u\n",
|
|
(unsigned) fYCbCrSubSampleH,
|
|
(unsigned) fYCbCrSubSampleV);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcYCbCrPositioning:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fYCbCrPositioning = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("YCbCrPositioning: %u\n",
|
|
(unsigned) fYCbCrPositioning);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcReferenceBlackWhite:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 6))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
fReferenceBlackWhite [j] = stream.TagValue_real64 (tagType);
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("ReferenceBlackWhite: %0.1f %0.1f %0.1f %0.1f %0.1f %0.1f\n",
|
|
fReferenceBlackWhite [0],
|
|
fReferenceBlackWhite [1],
|
|
fReferenceBlackWhite [2],
|
|
fReferenceBlackWhite [3],
|
|
fReferenceBlackWhite [4],
|
|
fReferenceBlackWhite [5]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCFARepeatPatternDim:
|
|
{
|
|
|
|
CheckCFA (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
fCFARepeatPatternRows = stream.TagValue_uint32 (tagType);
|
|
fCFARepeatPatternCols = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("CFARepeatPatternDim: Rows = %u, Cols = %u\n",
|
|
(unsigned) fCFARepeatPatternRows,
|
|
(unsigned) fCFARepeatPatternCols);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCFAPattern:
|
|
{
|
|
|
|
CheckCFA (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount,
|
|
SafeUint32Mult(fCFARepeatPatternRows, fCFARepeatPatternCols)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern ||
|
|
fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Note that the Exif spec stores this array in a different
|
|
// scan order than the TIFF-EP spec.
|
|
|
|
for (j = 0; j < fCFARepeatPatternRows; j++)
|
|
for (k = 0; k < fCFARepeatPatternCols; k++)
|
|
{
|
|
|
|
fCFAPattern [j] [k] = stream.Get_uint8 ();
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("CFAPattern:\n");
|
|
|
|
for (j = 0; j < fCFARepeatPatternRows; j++)
|
|
{
|
|
|
|
int32 spaces = 4;
|
|
|
|
for (k = 0; k < fCFARepeatPatternCols; k++)
|
|
{
|
|
|
|
while (spaces-- > 0)
|
|
{
|
|
printf (" ");
|
|
}
|
|
|
|
const char *name = LookupCFAColor (fCFAPattern [j] [k]);
|
|
|
|
spaces = 9 - (int32) strlen (name);
|
|
|
|
printf ("%s", name);
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCFAPlaneColor:
|
|
{
|
|
|
|
CheckCFA (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 3, kMaxColorPlanes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (j = 0; j < kMaxColorPlanes; j++)
|
|
{
|
|
|
|
if (j < tagCount)
|
|
fCFAPlaneColor [j] = stream.Get_uint8 ();
|
|
|
|
else
|
|
fCFAPlaneColor [j] = 255;
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("CFAPlaneColor:");
|
|
|
|
for (j = 0; j < tagCount; j++)
|
|
{
|
|
|
|
printf (" %s", LookupCFAColor (fCFAPlaneColor [j]));
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCFALayout:
|
|
{
|
|
|
|
CheckCFA (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fCFALayout = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("CFALayout: %s\n",
|
|
LookupCFALayout (fCFALayout));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcLinearizationTable:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
fLinearizationTableType = tagType;
|
|
fLinearizationTableCount = tagCount;
|
|
fLinearizationTableOffset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
DumpTagValues (stream,
|
|
"Table",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBlackLevelRepeatDim:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
fBlackLevelRepeatRows = stream.TagValue_uint32 (tagType);
|
|
fBlackLevelRepeatCols = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("BlackLevelRepeatDim: Rows = %u, Cols = %u\n",
|
|
(unsigned) fBlackLevelRepeatRows,
|
|
(unsigned) fBlackLevelRepeatCols);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBlackLevel:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, SafeUint32Mult(fBlackLevelRepeatRows,
|
|
fBlackLevelRepeatCols,
|
|
fSamplesPerPixel)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern ||
|
|
fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern ||
|
|
fSamplesPerPixel < 1 || fSamplesPerPixel > kMaxSamplesPerPixel)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (j = 0; j < fBlackLevelRepeatRows; j++)
|
|
for (k = 0; k < fBlackLevelRepeatCols; k++)
|
|
for (n = 0; n < fSamplesPerPixel; n++)
|
|
{
|
|
|
|
fBlackLevel [j] [k] [n] = stream.TagValue_real64 (tagType);
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("BlackLevel:");
|
|
|
|
if (fBlackLevelRepeatRows == 1 &&
|
|
fBlackLevelRepeatCols == 1)
|
|
{
|
|
|
|
for (n = 0; n < fSamplesPerPixel; n++)
|
|
{
|
|
printf (" %0.2f", fBlackLevel [0] [0] [n]);
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
printf ("\n");
|
|
|
|
for (n = 0; n < fSamplesPerPixel; n++)
|
|
{
|
|
|
|
if (fSamplesPerPixel > 1)
|
|
{
|
|
printf (" Sample: %u\n", (unsigned) n);
|
|
}
|
|
|
|
for (j = 0; j < fBlackLevelRepeatRows; j++)
|
|
{
|
|
|
|
printf (" ");
|
|
|
|
for (k = 0; k < fBlackLevelRepeatCols; k++)
|
|
{
|
|
|
|
printf (" %8.2f", fBlackLevel [j] [k] [n]);
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBlackLevelDeltaH:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttSRational);
|
|
|
|
fBlackLevelDeltaHType = tagType;
|
|
fBlackLevelDeltaHCount = tagCount;
|
|
fBlackLevelDeltaHOffset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
DumpTagValues (stream,
|
|
"Delta",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBlackLevelDeltaV:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttSRational);
|
|
|
|
fBlackLevelDeltaVType = tagType;
|
|
fBlackLevelDeltaVCount = tagCount;
|
|
fBlackLevelDeltaVOffset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
DumpTagValues (stream,
|
|
"Delta",
|
|
parentCode,
|
|
tagCode,
|
|
tagType,
|
|
tagCount);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcWhiteLevel:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel))
|
|
return false;
|
|
|
|
for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
|
|
fWhiteLevel [j] = stream.TagValue_real64 (tagType);
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("WhiteLevel:");
|
|
|
|
for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
|
|
printf (" %0.0f", fWhiteLevel [j]);
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcDefaultScale:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
return false;
|
|
|
|
fDefaultScaleH = stream.TagValue_urational (tagType);
|
|
fDefaultScaleV = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("DefaultScale: H = %0.4f V = %0.4f\n",
|
|
fDefaultScaleH.As_real64 (),
|
|
fDefaultScaleV.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcDefaultCropOrigin:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
return false;
|
|
|
|
fDefaultCropOriginH = stream.TagValue_urational (tagType);
|
|
fDefaultCropOriginV = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("DefaultCropOrigin: H = %0.2f V = %0.2f\n",
|
|
fDefaultCropOriginH.As_real64 (),
|
|
fDefaultCropOriginV.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcDefaultCropSize:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
return false;
|
|
|
|
fDefaultCropSizeH = stream.TagValue_urational (tagType);
|
|
fDefaultCropSizeV = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("DefaultCropSize: H = %0.2f V = %0.2f\n",
|
|
fDefaultCropSizeH.As_real64 (),
|
|
fDefaultCropSizeV.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcDefaultUserCrop:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
|
|
return false;
|
|
|
|
fDefaultUserCropT = stream.TagValue_urational (tagType);
|
|
fDefaultUserCropL = stream.TagValue_urational (tagType);
|
|
fDefaultUserCropB = stream.TagValue_urational (tagType);
|
|
fDefaultUserCropR = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("DefaultUserCrop: T = %0.2lf L = %0.2lf B = %0.2lf R = %0.2lf\n",
|
|
(double) fDefaultUserCropT.As_real64 (),
|
|
(double) fDefaultUserCropL.As_real64 (),
|
|
(double) fDefaultUserCropB.As_real64 (),
|
|
(double) fDefaultUserCropR.As_real64 ());
|
|
|
|
|
|
}
|
|
|
|
#endif // qDNGValidate
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBayerGreenSplit:
|
|
{
|
|
|
|
CheckCFA (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fBayerGreenSplit = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
printf ("BayerGreenSplit: %u\n", (unsigned) fBayerGreenSplit);
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcChromaBlurRadius:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fChromaBlurRadius = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("ChromaBlurRadius: %0.2f\n",
|
|
fChromaBlurRadius.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcAntiAliasStrength:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fAntiAliasStrength = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("AntiAliasStrength: %0.2f\n",
|
|
fAntiAliasStrength.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcBestQualityScale:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttRational);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fBestQualityScale = stream.TagValue_urational (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("BestQualityScale: %0.4f\n",
|
|
fBestQualityScale.As_real64 ());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcActiveArea:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
|
|
return false;
|
|
|
|
fActiveArea.t = stream.TagValue_int32 (tagType);
|
|
fActiveArea.l = stream.TagValue_int32 (tagType);
|
|
fActiveArea.b = stream.TagValue_int32 (tagType);
|
|
fActiveArea.r = stream.TagValue_int32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("ActiveArea: T = %d L = %d B = %d R = %d\n",
|
|
(int) fActiveArea.t,
|
|
(int) fActiveArea.l,
|
|
(int) fActiveArea.b,
|
|
(int) fActiveArea.r);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcMaskedAreas:
|
|
{
|
|
|
|
CheckMainIFD (parentCode, tagCode, fNewSubFileType);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
uint32 rect_count = tagCount / 4;
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, rect_count * 4))
|
|
return false;
|
|
|
|
fMaskedAreaCount = rect_count;
|
|
|
|
if (fMaskedAreaCount > kMaxMaskedAreas)
|
|
fMaskedAreaCount = kMaxMaskedAreas;
|
|
|
|
for (j = 0; j < fMaskedAreaCount; j++)
|
|
{
|
|
|
|
fMaskedArea [j].t = stream.TagValue_int32 (tagType);
|
|
fMaskedArea [j].l = stream.TagValue_int32 (tagType);
|
|
fMaskedArea [j].b = stream.TagValue_int32 (tagType);
|
|
fMaskedArea [j].r = stream.TagValue_int32 (tagType);
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("MaskedAreas: %u\n", (unsigned) fMaskedAreaCount);
|
|
|
|
for (j = 0; j < fMaskedAreaCount; j++)
|
|
{
|
|
|
|
printf (" Area [%u]: T = %d L = %d B = %d R = %d\n",
|
|
(unsigned) j,
|
|
(int) fMaskedArea [j].t,
|
|
(int) fMaskedArea [j].l,
|
|
(int) fMaskedArea [j].b,
|
|
(int) fMaskedArea [j].r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewApplicationName:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
|
|
|
|
ParseStringTag (stream,
|
|
parentCode,
|
|
tagCode,
|
|
tagCount,
|
|
fPreviewInfo.fApplicationName,
|
|
false);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewApplicationName: ");
|
|
|
|
DumpString (fPreviewInfo.fApplicationName);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewApplicationVersion:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
|
|
|
|
ParseStringTag (stream,
|
|
parentCode,
|
|
tagCode,
|
|
tagCount,
|
|
fPreviewInfo.fApplicationVersion,
|
|
false);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewApplicationVersion: ");
|
|
|
|
DumpString (fPreviewInfo.fApplicationVersion);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewSettingsName:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte);
|
|
|
|
ParseStringTag (stream,
|
|
parentCode,
|
|
tagCode,
|
|
tagCount,
|
|
fPreviewInfo.fSettingsName,
|
|
false);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewSettingsName: ");
|
|
|
|
DumpString (fPreviewInfo.fSettingsName);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewSettingsDigest:
|
|
{
|
|
|
|
if (!CheckTagType (parentCode, tagCode, tagType, ttByte))
|
|
return false;
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 16))
|
|
return false;
|
|
|
|
stream.Get (fPreviewInfo.fSettingsDigest.data, 16);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewSettingsDigest: ");
|
|
|
|
DumpFingerprint (fPreviewInfo.fSettingsDigest);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewColorSpace:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
CheckTagCount (parentCode, tagCode, tagCount, 1);
|
|
|
|
fPreviewInfo.fColorSpace = (PreviewColorSpaceEnum)
|
|
stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewColorSpace: %s\n",
|
|
LookupPreviewColorSpace ((uint32) fPreviewInfo.fColorSpace));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcPreviewDateTime:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttAscii);
|
|
|
|
ParseStringTag (stream,
|
|
parentCode,
|
|
tagCode,
|
|
tagCount,
|
|
fPreviewInfo.fDateTime,
|
|
false);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("PreviewDateTime: ");
|
|
|
|
DumpString (fPreviewInfo.fDateTime);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcRowInterleaveFactor:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 1))
|
|
return false;
|
|
|
|
fRowInterleaveFactor = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("RowInterleaveFactor: %u\n",
|
|
(unsigned) fRowInterleaveFactor);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcSubTileBlockSize:
|
|
{
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 2))
|
|
return false;
|
|
|
|
fSubTileBlockRows = stream.TagValue_uint32 (tagType);
|
|
fSubTileBlockCols = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("SubTileBlockSize: rows = %u, cols = %u\n",
|
|
(unsigned) fSubTileBlockRows,
|
|
(unsigned) fSubTileBlockCols);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcOpcodeList1:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
|
|
|
|
fOpcodeList1Count = tagCount;
|
|
fOpcodeList1Offset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("OpcodeList1: count = %u, offset = %u\n",
|
|
(unsigned) fOpcodeList1Count,
|
|
(unsigned) fOpcodeList1Offset);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcOpcodeList2:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
|
|
|
|
fOpcodeList2Count = tagCount;
|
|
fOpcodeList2Offset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("OpcodeList2: count = %u, offset = %u\n",
|
|
(unsigned) fOpcodeList2Count,
|
|
(unsigned) fOpcodeList2Offset);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcOpcodeList3:
|
|
{
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttUndefined);
|
|
|
|
fOpcodeList3Count = tagCount;
|
|
fOpcodeList3Offset = tagOffset;
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("OpcodeList3: count = %u, offset = %u\n",
|
|
(unsigned) fOpcodeList3Count,
|
|
(unsigned) fOpcodeList3Offset);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcRawToPreviewGain:
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
if (fNewSubFileType != sfPreviewImage)
|
|
{
|
|
|
|
char message [256];
|
|
|
|
sprintf (message,
|
|
"%s %s is not allowed IFDs with NewSubFileType != PreviewImage",
|
|
LookupParentCode (parentCode),
|
|
LookupTagCode (parentCode, tagCode));
|
|
|
|
ReportWarning (message);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttDouble);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 1))
|
|
return false;
|
|
|
|
fPreviewInfo.fRawToPreviewGain = stream.TagValue_real64 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("RawToPreviewGain = %f\n",
|
|
fPreviewInfo.fRawToPreviewGain);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tcCacheVersion:
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
if (fNewSubFileType != sfPreviewImage)
|
|
{
|
|
|
|
char message [256];
|
|
|
|
sprintf (message,
|
|
"%s %s is not allowed IFDs with NewSubFileType != PreviewImage",
|
|
LookupParentCode (parentCode),
|
|
LookupTagCode (parentCode, tagCode));
|
|
|
|
ReportWarning (message);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation);
|
|
|
|
CheckTagType (parentCode, tagCode, tagType, ttLong);
|
|
|
|
if (!CheckTagCount (parentCode, tagCode, tagCount, 1))
|
|
return false;
|
|
|
|
fPreviewInfo.fCacheVersion = stream.TagValue_uint32 (tagType);
|
|
|
|
#if qDNGValidate
|
|
|
|
if (gVerbose)
|
|
{
|
|
|
|
printf ("CacheVersion = 0x%x\n",
|
|
(unsigned) fPreviewInfo.fCacheVersion);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_ifd::PostParse ()
|
|
{
|
|
|
|
uint32 j;
|
|
uint32 k;
|
|
|
|
// There is only one PlanarConfiguration for single sample imaages.
|
|
|
|
if (fSamplesPerPixel == 1)
|
|
{
|
|
fPlanarConfiguration = pcInterleaved;
|
|
}
|
|
|
|
// Default tile size.
|
|
|
|
if (fTileWidth == 0)
|
|
{
|
|
fTileWidth = fImageWidth;
|
|
}
|
|
|
|
if (fTileLength == 0)
|
|
{
|
|
fTileLength = fImageLength;
|
|
}
|
|
|
|
// Default ActiveArea.
|
|
|
|
dng_rect imageArea (0, 0, fImageLength, fImageWidth);
|
|
|
|
if (fActiveArea.IsZero ())
|
|
{
|
|
fActiveArea = imageArea;
|
|
}
|
|
|
|
// Default crop size.
|
|
|
|
if (fDefaultCropSizeH.d == 0)
|
|
{
|
|
fDefaultCropSizeH = dng_urational (fActiveArea.W (), 1);
|
|
}
|
|
|
|
if (fDefaultCropSizeV.d == 0)
|
|
{
|
|
fDefaultCropSizeV = dng_urational (fActiveArea.H (), 1);
|
|
}
|
|
|
|
// Default white level.
|
|
|
|
uint32 defaultWhite = (fSampleFormat [0] == sfFloatingPoint) ?
|
|
1 :
|
|
(uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1);
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
|
|
if (fWhiteLevel [j] < 0.0)
|
|
{
|
|
fWhiteLevel [j] = (real64) defaultWhite;
|
|
}
|
|
|
|
}
|
|
|
|
// Check AntiAliasStrength.
|
|
|
|
if (fAntiAliasStrength.As_real64 () < 0.0 ||
|
|
fAntiAliasStrength.As_real64 () > 1.0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportWarning ("Invalid AntiAliasStrength");
|
|
|
|
#endif
|
|
|
|
fAntiAliasStrength = dng_urational (1, 1);
|
|
|
|
}
|
|
|
|
// Check MaskedAreas.
|
|
|
|
for (j = 0; j < fMaskedAreaCount; j++)
|
|
{
|
|
|
|
const dng_rect &r = fMaskedArea [j];
|
|
|
|
if (r.IsEmpty () || ((r & imageArea) != r))
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportWarning ("Invalid MaskedArea");
|
|
|
|
#endif
|
|
|
|
fMaskedAreaCount = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((r & fActiveArea).NotEmpty ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportWarning ("MaskedArea overlaps ActiveArea");
|
|
|
|
#endif
|
|
|
|
fMaskedAreaCount = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (k = 0; k < j; k++)
|
|
{
|
|
|
|
if ((r & fMaskedArea [k]).NotEmpty ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportWarning ("MaskedAreas overlap each other");
|
|
|
|
#endif
|
|
|
|
fMaskedAreaCount = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_ifd::IsValidCFA (dng_shared &shared,
|
|
uint32 parentCode)
|
|
{
|
|
|
|
uint32 j;
|
|
uint32 k;
|
|
uint32 n;
|
|
|
|
#if !qDNGValidate
|
|
|
|
(void) parentCode; // Unused
|
|
|
|
#endif
|
|
|
|
if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern ||
|
|
fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid CFAPatternRepeatDim",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uint32 count [kMaxColorPlanes];
|
|
|
|
for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++)
|
|
{
|
|
count [n] = 0;
|
|
}
|
|
|
|
for (j = 0; j < fCFARepeatPatternRows; j++)
|
|
{
|
|
|
|
for (k = 0; k < fCFARepeatPatternCols; k++)
|
|
{
|
|
|
|
bool found = false;
|
|
|
|
for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++)
|
|
{
|
|
|
|
if (fCFAPattern [j] [k] == fCFAPlaneColor [n])
|
|
{
|
|
found = true;
|
|
count [n] ++;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("CFAPattern contains colors not included in the CFAPlaneColor tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++)
|
|
{
|
|
|
|
if (count [n] == 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("CFAPattern does not contain all the colors in the CFAPlaneColor tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fCFALayout < 1 || fCFALayout > 9)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid CFALayout",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_ifd::IsValidDNG (dng_shared &shared,
|
|
uint32 parentCode)
|
|
{
|
|
|
|
uint32 j;
|
|
|
|
bool isFloatingPoint = (fSampleFormat [0] == sfFloatingPoint);
|
|
|
|
dng_rect imageArea (0, 0, fImageLength, fImageWidth);
|
|
|
|
uint32 defaultWhite = isFloatingPoint ?
|
|
1 :
|
|
(uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1);
|
|
|
|
bool isMonochrome = (shared.fCameraProfile.fColorPlanes == 1);
|
|
bool isColor = !isMonochrome;
|
|
|
|
bool isMainIFD = (fNewSubFileType == sfMainImage);
|
|
|
|
// Check NewSubFileType.
|
|
|
|
if (!fUsesNewSubFileType)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing NewSubFileType",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fNewSubFileType != sfMainImage &&
|
|
fNewSubFileType != sfPreviewImage &&
|
|
fNewSubFileType != sfTransparencyMask &&
|
|
fNewSubFileType != sfPreviewMask &&
|
|
fNewSubFileType != sfAltPreviewImage)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unexpected NewSubFileType",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check ImageWidth and ImageLength.
|
|
|
|
if (fImageWidth < 1)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid ImageWidth",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fImageLength < 1)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid ImageLength",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fImageWidth > kMaxImageSide ||
|
|
fImageLength > kMaxImageSide)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportWarning ("Image size is larger than supported");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check PhotometricInterpretation.
|
|
|
|
if (fNewSubFileType == sfTransparencyMask ||
|
|
fNewSubFileType == sfPreviewMask)
|
|
{
|
|
|
|
if (fPhotometricInterpretation != piTransparencyMask)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("NewSubFileType requires PhotometricInterpretation = TransparencyMask",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
switch (fPhotometricInterpretation)
|
|
{
|
|
|
|
case piBlackIsZero:
|
|
case piRGB:
|
|
case piYCbCr:
|
|
{
|
|
|
|
if (isMainIFD)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("PhotometricInterpretation requires NewSubFileType = 1",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case piCFA:
|
|
{
|
|
|
|
if (!isMainIFD)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("PhotometricInterpretation requires NewSubFileType = 0",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case piLinearRaw:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid PhotometricInterpretation",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (fPhotometricInterpretation)
|
|
{
|
|
|
|
case piBlackIsZero:
|
|
{
|
|
|
|
// Allow black in white previews even in color images since the
|
|
// raw processing software may be converting to grayscale.
|
|
|
|
if (isColor && isMainIFD)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("PhotometricInterpretation forbids use of ColorMatrix1 tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case piRGB:
|
|
case piYCbCr:
|
|
{
|
|
|
|
// Allow color previews even in monochrome DNG files, since the
|
|
// raw procesing software may be adding color effects.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case piCFA:
|
|
{
|
|
|
|
if (isMonochrome)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("PhotometricInterpretation requires use of ColorMatrix1 tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isFloatingPoint)
|
|
{
|
|
|
|
if (fPhotometricInterpretation != piCFA &&
|
|
fPhotometricInterpretation != piLinearRaw &&
|
|
fPhotometricInterpretation != piTransparencyMask)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Floating point data requires PhotometricInterpretation CFA or LinearRaw or TransparencyMask",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check SamplesPerPixel and BitsPerSample.
|
|
|
|
uint32 minSamplesPerPixel = 1;
|
|
uint32 maxSamplesPerPixel = 1;
|
|
|
|
uint32 minBitsPerSample = 8;
|
|
uint32 maxBitsPerSample = 16;
|
|
|
|
switch (fPhotometricInterpretation)
|
|
{
|
|
|
|
case piBlackIsZero:
|
|
break;
|
|
|
|
case piRGB:
|
|
case piYCbCr:
|
|
{
|
|
minSamplesPerPixel = 3;
|
|
maxSamplesPerPixel = 3;
|
|
break;
|
|
}
|
|
|
|
case piCFA:
|
|
{
|
|
maxSamplesPerPixel = kMaxSamplesPerPixel;
|
|
maxBitsPerSample = 32;
|
|
break;
|
|
}
|
|
|
|
case piLinearRaw:
|
|
{
|
|
minSamplesPerPixel = shared.fCameraProfile.fColorPlanes;
|
|
maxSamplesPerPixel = shared.fCameraProfile.fColorPlanes;
|
|
maxBitsPerSample = 32;
|
|
break;
|
|
}
|
|
|
|
case piTransparencyMask:
|
|
{
|
|
minBitsPerSample = 8;
|
|
maxBitsPerSample = 16;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (isFloatingPoint)
|
|
{
|
|
minBitsPerSample = 16;
|
|
maxBitsPerSample = 32;
|
|
}
|
|
|
|
if (fSamplesPerPixel < minSamplesPerPixel ||
|
|
fSamplesPerPixel > maxSamplesPerPixel)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid SamplesPerPixel",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (j = 0; j < kMaxSamplesPerPixel; j++)
|
|
{
|
|
|
|
if (j < fSamplesPerPixel)
|
|
{
|
|
|
|
if (fBitsPerSample [j] < minBitsPerSample ||
|
|
fBitsPerSample [j] > maxBitsPerSample)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid BitsPerSample",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isFloatingPoint &&
|
|
fBitsPerSample [j] != 16 &&
|
|
fBitsPerSample [j] != 24 &&
|
|
fBitsPerSample [j] != 32)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BitsPerSample for floating point",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (minBitsPerSample == 8 &&
|
|
maxBitsPerSample == 16 &&
|
|
fBitsPerSample [j] != 8 &&
|
|
fBitsPerSample [j] != 16)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Rendered previews and integer masks require 8 or 16 bits per sample",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (j > 0 && fBitsPerSample [j] != fBitsPerSample [0])
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("BitsPerSample not equal for all samples",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
if (fBitsPerSample [j] != 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Too many values specified in BitsPerSample",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check Compression.
|
|
|
|
switch (fCompression)
|
|
{
|
|
|
|
case ccUncompressed:
|
|
break;
|
|
|
|
case ccJPEG:
|
|
{
|
|
|
|
if (fPhotometricInterpretation == piRGB)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("JPEG previews should use PhotometricInterpretation = YCbYb",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fBitsPerSample [0] > 16)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("JPEG compression is limited to 16 bits/sample",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ccLossyJPEG:
|
|
{
|
|
|
|
if (fPhotometricInterpretation != piLinearRaw)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Lossy JPEG compression code requires PhotometricInterpretation = LinearRaw",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fBitsPerSample [0] != 8)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Lossy JPEG compression is limited to 8 bits/sample",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ccDeflate:
|
|
{
|
|
|
|
if (!isFloatingPoint &&
|
|
fBitsPerSample [0] != 32 &&
|
|
fPhotometricInterpretation != piTransparencyMask)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("ZIP compression is limited to floating point and 32-bit integer and transparency masks",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported Compression",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check Predictor.
|
|
|
|
if (isFloatingPoint && fCompression == ccDeflate &&
|
|
(fPredictor == cpFloatingPoint ||
|
|
fPredictor == cpFloatingPointX2 ||
|
|
fPredictor == cpFloatingPointX4))
|
|
{
|
|
|
|
// These combinations are supported.
|
|
|
|
}
|
|
|
|
else if (!isFloatingPoint && fCompression == ccDeflate &&
|
|
(fPredictor == cpHorizontalDifference ||
|
|
fPredictor == cpHorizontalDifferenceX2 ||
|
|
fPredictor == cpHorizontalDifferenceX4))
|
|
{
|
|
|
|
// These combinations are supported.
|
|
|
|
}
|
|
|
|
else if (fPredictor != cpNullPredictor)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported Predictor",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check FillOrder.
|
|
|
|
if (fFillOrder != 1)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported FillOrder",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check PlanarConfiguration.
|
|
|
|
if (fPlanarConfiguration != pcInterleaved)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported PlanarConfiguration",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check ExtraSamples.
|
|
|
|
if (fExtraSamplesCount != 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported ExtraSamples",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check SampleFormat.
|
|
|
|
for (j = 0; j < fSamplesPerPixel; j++)
|
|
{
|
|
|
|
if (fSampleFormat [j] != (isFloatingPoint ? sfFloatingPoint : sfUnsignedInteger))
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unsupported SampleFormat",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check Orientation.
|
|
|
|
if (fOrientation > 9)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Unknown Orientation",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (fOrientation != 0 && parentCode != 0)
|
|
{
|
|
|
|
ReportWarning ("Unexpected Orientation tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
}
|
|
|
|
if (fOrientation == 0 && parentCode == 0)
|
|
{
|
|
|
|
ReportWarning ("Missing Orientation tag",
|
|
LookupParentCode (parentCode));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// Check Strips vs. Tiles.
|
|
|
|
if (!fUsesStrips && !fUsesTiles)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("IFD uses neither strips nor tiles",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fUsesStrips && fUsesTiles)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("IFD uses both strips and tiles",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check tile info.
|
|
|
|
uint32 tilesWide = SafeUint32DivideUp(fImageWidth, fTileWidth);
|
|
uint32 tilesHigh = SafeUint32DivideUp(fImageLength, fTileLength);
|
|
|
|
uint32 tileCount = tilesWide * tilesHigh;
|
|
|
|
if (fTileOffsetsCount != tileCount)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid Strip/TileOffsets",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fTileByteCountsCount != tileCount)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Missing or invalid Strip/TileByteCounts",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check CFA pattern.
|
|
|
|
if (fPhotometricInterpretation == piCFA)
|
|
{
|
|
|
|
if (!IsValidCFA (shared, parentCode))
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check ActiveArea.
|
|
|
|
if (((fActiveArea & imageArea) != fActiveArea) || fActiveArea.IsEmpty ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid ActiveArea",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fActiveArea != imageArea)
|
|
{
|
|
|
|
if (shared.fDNGBackwardVersion < dngVersion_1_1_0_0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Non-default ActiveArea tag not allowed in this DNG version",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check LinearizationTable.
|
|
|
|
if (fLinearizationTableCount)
|
|
{
|
|
|
|
if (fLinearizationTableType != ttShort)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalidate LinearizationTable type",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fLinearizationTableCount < 2 ||
|
|
fLinearizationTableCount > 65536)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalidate LinearizationTable count",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isFloatingPoint || fBitsPerSample [0] > 16)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Linearization table not allowed for this data type",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check BlackLevelRepeatDim.
|
|
|
|
if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern ||
|
|
fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BlackLevelRepeatDim",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check BlackLevelDeltaH.
|
|
|
|
if (fBlackLevelDeltaHCount != 0 &&
|
|
fBlackLevelDeltaHCount != fActiveArea.W ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BlackLevelDeltaH count",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check BlackLevelDeltaV.
|
|
|
|
if (fBlackLevelDeltaVCount != 0 &&
|
|
fBlackLevelDeltaVCount != fActiveArea.H ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BlackLevelDeltaV count",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check WhiteLevel.
|
|
|
|
real64 maxWhite = fLinearizationTableCount ? 65535.0
|
|
: (real64) defaultWhite;
|
|
|
|
for (j = 0; j < fSamplesPerPixel; j++)
|
|
{
|
|
|
|
if (fWhiteLevel [j] < 1.0 || (fWhiteLevel [j] > maxWhite && !isFloatingPoint))
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid WhiteLevel",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check BlackLevel.
|
|
|
|
for (j = 0; j < kMaxBlackPattern; j++)
|
|
{
|
|
|
|
for (uint32 k = 0; k < kMaxBlackPattern; k++)
|
|
{
|
|
|
|
for (uint32 s = 0; s < kMaxSamplesPerPixel; s++)
|
|
{
|
|
|
|
const real64 black = fBlackLevel [j][k][s];
|
|
|
|
if (black >= fWhiteLevel [s])
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BlackLevel",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check DefaultScale.
|
|
|
|
if (fDefaultScaleH.As_real64 () <= 0.0 ||
|
|
fDefaultScaleV.As_real64 () <= 0.0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid DefaultScale");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check BestQualityScale.
|
|
|
|
if (fBestQualityScale.As_real64 () < 1.0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid BestQualityScale");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check DefaultCropOrigin.
|
|
|
|
if (fDefaultCropOriginH.As_real64 () < 0.0 ||
|
|
fDefaultCropOriginV.As_real64 () < 0.0 ||
|
|
fDefaultCropOriginH.As_real64 () >= (real64) fActiveArea.W () ||
|
|
fDefaultCropOriginV.As_real64 () >= (real64) fActiveArea.H ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid DefaultCropOrigin");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check DefaultCropSize.
|
|
|
|
if (fDefaultCropSizeH.As_real64 () <= 0.0 ||
|
|
fDefaultCropSizeV.As_real64 () <= 0.0 ||
|
|
fDefaultCropSizeH.As_real64 () > (real64) fActiveArea.W () ||
|
|
fDefaultCropSizeV.As_real64 () > (real64) fActiveArea.H ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid DefaultCropSize");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check DefaultCrop area.
|
|
|
|
if (fDefaultCropOriginH.As_real64 () +
|
|
fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () ||
|
|
fDefaultCropOriginV.As_real64 () +
|
|
fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Default crop extends outside ActiveArea");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check DefaultUserCrop.
|
|
|
|
if (fDefaultUserCropT.As_real64 () < 0.0 ||
|
|
fDefaultUserCropL.As_real64 () < 0.0 ||
|
|
fDefaultUserCropB.As_real64 () > 1.0 ||
|
|
fDefaultUserCropR.As_real64 () > 1.0 ||
|
|
fDefaultUserCropT.As_real64 () >= fDefaultUserCropB.As_real64 () ||
|
|
fDefaultUserCropL.As_real64 () >= fDefaultUserCropR.As_real64 ())
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Invalid DefaultUserCrop");
|
|
|
|
#endif // qDNGValidate
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// The default crop and default user crop tags are not allowed for the
|
|
// non-main image. If they are there, at least require that they be NOPs.
|
|
|
|
if (!isMainIFD)
|
|
{
|
|
|
|
if (Round_int32 (fDefaultCropOriginH.As_real64 ()) != 0 ||
|
|
Round_int32 (fDefaultCropOriginV.As_real64 ()) != 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("non-default DefaultCropOrigin on non-main image");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Round_int32 (fDefaultCropSizeH.As_real64 ()) != (int32) fImageWidth ||
|
|
Round_int32 (fDefaultCropSizeV.As_real64 ()) != (int32) fImageLength)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("non-default DefaultCropSize on non-main image");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fDefaultUserCropT.As_real64 () != 0.0 ||
|
|
fDefaultUserCropL.As_real64 () != 0.0 ||
|
|
fDefaultUserCropB.As_real64 () != 1.0 ||
|
|
fDefaultUserCropR.As_real64 () != 1.0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("non-default DefaultCUserCrop on non-main image");
|
|
|
|
#endif // qDNGValidate
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Warning if too little padding on CFA image.
|
|
|
|
#if qDNGValidate
|
|
|
|
if (fPhotometricInterpretation == piCFA)
|
|
{
|
|
|
|
const real64 kMinPad = 1.9;
|
|
|
|
if (fDefaultCropOriginH.As_real64 () < kMinPad)
|
|
{
|
|
|
|
ReportWarning ("Too little padding on left edge of CFA image",
|
|
"possible interpolation artifacts");
|
|
|
|
}
|
|
|
|
if (fDefaultCropOriginV.As_real64 () < kMinPad)
|
|
{
|
|
|
|
ReportWarning ("Too little padding on top edge of CFA image",
|
|
"possible interpolation artifacts");
|
|
|
|
}
|
|
|
|
if (fDefaultCropOriginH.As_real64 () +
|
|
fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () - kMinPad)
|
|
{
|
|
|
|
ReportWarning ("Too little padding on right edge of CFA image",
|
|
"possible interpolation artifacts");
|
|
|
|
}
|
|
|
|
if (fDefaultCropOriginV.As_real64 () +
|
|
fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H () - kMinPad)
|
|
{
|
|
|
|
ReportWarning ("Too little padding on bottom edge of CFA image",
|
|
"possible interpolation artifacts");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// Check RowInterleaveFactor
|
|
|
|
if (fRowInterleaveFactor != 1)
|
|
{
|
|
|
|
if (fRowInterleaveFactor < 1 ||
|
|
fRowInterleaveFactor > fImageLength)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("RowInterleaveFactor out of valid range",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Non-default RowInterleaveFactor tag not allowed in this DNG version",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check SubTileBlockSize
|
|
|
|
if (fSubTileBlockRows != 1 || fSubTileBlockCols != 1)
|
|
{
|
|
|
|
if (fSubTileBlockRows < 2 || fSubTileBlockRows > fTileLength ||
|
|
fSubTileBlockCols < 1 || fSubTileBlockCols > fTileWidth)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("SubTileBlockSize out of valid range",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((fTileLength % fSubTileBlockRows) != 0 ||
|
|
(fTileWidth % fSubTileBlockCols) != 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("TileSize not exact multiple of SubTileBlockSize",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Non-default SubTileBlockSize tag not allowed in this DNG version",
|
|
LookupParentCode (parentCode));
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_ifd::TilesAcross () const
|
|
{
|
|
|
|
if (fTileWidth)
|
|
{
|
|
|
|
return (SafeUint32Sub(SafeUint32Add(fImageWidth, fTileWidth), 1)) / fTileWidth;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_ifd::TilesDown () const
|
|
{
|
|
|
|
if (fTileLength)
|
|
{
|
|
|
|
return (SafeUint32Sub(SafeUint32Add(fImageLength, fTileLength), 1)) / fTileLength;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_ifd::TilesPerImage () const
|
|
{
|
|
|
|
uint32 total = TilesAcross () * TilesDown ();
|
|
|
|
if (fPlanarConfiguration == pcPlanar)
|
|
{
|
|
|
|
total *= fSamplesPerPixel;
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_rect dng_ifd::TileArea (uint32 rowIndex,
|
|
uint32 colIndex) const
|
|
{
|
|
|
|
dng_rect r;
|
|
|
|
r.t = rowIndex * fTileLength;
|
|
r.b = r.t + fTileLength;
|
|
|
|
r.l = colIndex * fTileWidth;
|
|
r.r = r.l + fTileWidth;
|
|
|
|
// If this IFD is using strips rather than tiles, the last strip
|
|
// is trimmed so it does not extend beyond the end of the image.
|
|
|
|
if (fUsesStrips)
|
|
{
|
|
|
|
r.b = Min_uint32 (r.b, fImageLength);
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_ifd::TileByteCount (const dng_rect &tile) const
|
|
{
|
|
|
|
if (fCompression == ccUncompressed)
|
|
{
|
|
|
|
uint32 bitsPerRow = SafeUint32Mult(tile.W (), fBitsPerSample [0]);
|
|
|
|
if (fPlanarConfiguration == pcInterleaved)
|
|
{
|
|
|
|
bitsPerRow = SafeUint32Mult(bitsPerRow, fSamplesPerPixel);
|
|
|
|
}
|
|
|
|
uint32 bytesPerRow = SafeUint32DivideUp(bitsPerRow, 8);
|
|
|
|
if (fPlanarConfiguration == pcRowInterleaved)
|
|
{
|
|
|
|
bytesPerRow = SafeUint32Mult(bytesPerRow, fSamplesPerPixel);
|
|
|
|
}
|
|
|
|
return SafeUint32Mult(bytesPerRow, tile.H ());
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_ifd::SetSingleStrip ()
|
|
{
|
|
|
|
fTileWidth = fImageWidth;
|
|
fTileLength = fImageLength;
|
|
|
|
fUsesTiles = false;
|
|
fUsesStrips = true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_ifd::FindTileSize (uint32 bytesPerTile,
|
|
uint32 cellH,
|
|
uint32 cellV)
|
|
{
|
|
|
|
uint32 bytesPerSample = fSamplesPerPixel *
|
|
((fBitsPerSample [0] + 7) >> 3);
|
|
|
|
uint32 samplesPerTile = bytesPerTile / bytesPerSample;
|
|
|
|
uint32 tileSide = Round_uint32 (sqrt ((real64) samplesPerTile));
|
|
|
|
fTileWidth = Min_uint32 (fImageWidth, tileSide);
|
|
|
|
uint32 across = TilesAcross ();
|
|
|
|
fTileWidth = (fImageWidth + across - 1) / across;
|
|
|
|
fTileWidth = ((fTileWidth + cellH - 1) / cellH) * cellH;
|
|
|
|
fTileLength = Pin_uint32 (1,
|
|
samplesPerTile / fTileWidth,
|
|
fImageLength);
|
|
|
|
uint32 down = TilesDown ();
|
|
|
|
fTileLength = (fImageLength + down - 1) / down;
|
|
|
|
fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV;
|
|
|
|
fUsesTiles = true;
|
|
fUsesStrips = false;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_ifd::FindStripSize (uint32 bytesPerStrip,
|
|
uint32 cellV)
|
|
{
|
|
|
|
uint32 bytesPerSample = fSamplesPerPixel *
|
|
((fBitsPerSample [0] + 7) >> 3);
|
|
|
|
uint32 samplesPerStrip = bytesPerStrip / bytesPerSample;
|
|
|
|
fTileWidth = fImageWidth;
|
|
|
|
fTileLength = Pin_uint32 (1,
|
|
samplesPerStrip / fTileWidth,
|
|
fImageLength);
|
|
|
|
uint32 down = TilesDown ();
|
|
|
|
fTileLength = (fImageLength + down - 1) / down;
|
|
|
|
fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV;
|
|
|
|
fUsesTiles = false;
|
|
fUsesStrips = true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_ifd::PixelType () const
|
|
{
|
|
|
|
if (fSampleFormat [0] == sfFloatingPoint)
|
|
{
|
|
return ttFloat;
|
|
}
|
|
|
|
if (fBitsPerSample [0] <= 8)
|
|
{
|
|
return ttByte;
|
|
}
|
|
|
|
else if (fBitsPerSample [0] <= 16)
|
|
{
|
|
return ttShort;
|
|
}
|
|
|
|
return ttLong;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_ifd::IsBaselineJPEG () const
|
|
{
|
|
|
|
if (fBitsPerSample [0] != 8)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (fSampleFormat [0] != sfUnsignedInteger)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (fCompression == ccLossyJPEG)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (fCompression != ccJPEG)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (fPhotometricInterpretation)
|
|
{
|
|
|
|
case piBlackIsZero:
|
|
{
|
|
return (fSamplesPerPixel == 1);
|
|
}
|
|
|
|
case piYCbCr:
|
|
{
|
|
return (fSamplesPerPixel == 3 ) &&
|
|
(fPlanarConfiguration == pcInterleaved);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_ifd::CanRead () const
|
|
{
|
|
|
|
dng_read_image reader;
|
|
|
|
return reader.CanRead (*this);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_ifd::ReadImage (dng_host &host,
|
|
dng_stream &stream,
|
|
dng_image &image,
|
|
dng_jpeg_image *jpegImage,
|
|
dng_fingerprint *jpegDigest) const
|
|
{
|
|
|
|
dng_read_image reader;
|
|
|
|
reader.Read (host,
|
|
*this,
|
|
stream,
|
|
image,
|
|
jpegImage,
|
|
jpegDigest);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|