/*****************************************************************************/ // 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_camera_profile.cpp#1 $ */ /* $DateTime: 2012/05/30 13:28:51 $ */ /* $Change: 832332 $ */ /* $Author: tknoll $ */ #include "dng_camera_profile.h" #include "dng_1d_table.h" #include "dng_assertions.h" #include "dng_color_space.h" #include "dng_host.h" #include "dng_exceptions.h" #include "dng_image_writer.h" #include "dng_info.h" #include "dng_parse_utils.h" #include "dng_safe_arithmetic.h" #include "dng_tag_codes.h" #include "dng_tag_types.h" #include "dng_temperature.h" #include "dng_xy_coord.h" /*****************************************************************************/ const char * kProfileName_Embedded = "Embedded"; const char * kAdobeCalibrationSignature = "com.adobe"; /*****************************************************************************/ dng_camera_profile::dng_camera_profile () : fName () , fCalibrationIlluminant1 (lsUnknown) , fCalibrationIlluminant2 (lsUnknown) , fColorMatrix1 () , fColorMatrix2 () , fForwardMatrix1 () , fForwardMatrix2 () , fReductionMatrix1 () , fReductionMatrix2 () , fFingerprint () , fCopyright () , fEmbedPolicy (pepAllowCopying) , fHueSatDeltas1 () , fHueSatDeltas2 () , fHueSatMapEncoding (encoding_Linear) , fLookTable () , fLookTableEncoding (encoding_Linear) , fBaselineExposureOffset (0, 100) , fDefaultBlackRender (defaultBlackRender_Auto) , fToneCurve () , fProfileCalibrationSignature () , fUniqueCameraModelRestriction () , fWasReadFromDNG (false) , fWasReadFromDisk (false) , fWasBuiltinMatrix (false) , fWasStubbed (false) { fToneCurve.SetInvalid (); } /*****************************************************************************/ dng_camera_profile::~dng_camera_profile () { } /*****************************************************************************/ real64 dng_camera_profile::IlluminantToTemperature (uint32 light) { switch (light) { case lsStandardLightA: case lsTungsten: { return 2850.0; } case lsISOStudioTungsten: { return 3200.0; } case lsD50: { return 5000.0; } case lsD55: case lsDaylight: case lsFineWeather: case lsFlash: case lsStandardLightB: { return 5500.0; } case lsD65: case lsStandardLightC: case lsCloudyWeather: { return 6500.0; } case lsD75: case lsShade: { return 7500.0; } case lsDaylightFluorescent: { return (5700.0 + 7100.0) * 0.5; } case lsDayWhiteFluorescent: { return (4600.0 + 5500.0) * 0.5; } case lsCoolWhiteFluorescent: case lsFluorescent: { return (3800.0 + 4500.0) * 0.5; } case lsWhiteFluorescent: { return (3250.0 + 3800.0) * 0.5; } case lsWarmWhiteFluorescent: { return (2600.0 + 3250.0) * 0.5; } default: { return 0.0; } } } /******************************************************************************/ void dng_camera_profile::NormalizeColorMatrix (dng_matrix &m) { if (m.NotEmpty ()) { // Find scale factor to normalize the matrix. dng_vector coord = m * PCStoXYZ (); real64 maxCoord = coord.MaxEntry (); if (maxCoord > 0.0 && (maxCoord < 0.99 || maxCoord > 1.01)) { m.Scale (1.0 / maxCoord); } // Round to four decimal places. m.Round (10000); } } /******************************************************************************/ void dng_camera_profile::SetColorMatrix1 (const dng_matrix &m) { fColorMatrix1 = m; NormalizeColorMatrix (fColorMatrix1); ClearFingerprint (); } /******************************************************************************/ void dng_camera_profile::SetColorMatrix2 (const dng_matrix &m) { fColorMatrix2 = m; NormalizeColorMatrix (fColorMatrix2); ClearFingerprint (); } /******************************************************************************/ // Make sure the forward matrix maps to exactly the PCS. void dng_camera_profile::NormalizeForwardMatrix (dng_matrix &m) { if (m.NotEmpty ()) { dng_vector cameraOne; cameraOne.SetIdentity (m.Cols ()); dng_vector xyz = m * cameraOne; m = PCStoXYZ ().AsDiagonal () * Invert (xyz.AsDiagonal ()) * m; } } /******************************************************************************/ void dng_camera_profile::SetForwardMatrix1 (const dng_matrix &m) { fForwardMatrix1 = m; fForwardMatrix1.Round (10000); ClearFingerprint (); } /******************************************************************************/ void dng_camera_profile::SetForwardMatrix2 (const dng_matrix &m) { fForwardMatrix2 = m; fForwardMatrix2.Round (10000); ClearFingerprint (); } /*****************************************************************************/ void dng_camera_profile::SetReductionMatrix1 (const dng_matrix &m) { fReductionMatrix1 = m; fReductionMatrix1.Round (10000); ClearFingerprint (); } /******************************************************************************/ void dng_camera_profile::SetReductionMatrix2 (const dng_matrix &m) { fReductionMatrix2 = m; fReductionMatrix2.Round (10000); ClearFingerprint (); } /*****************************************************************************/ bool dng_camera_profile::HasColorMatrix1 () const { return fColorMatrix1.Cols () == 3 && fColorMatrix1.Rows () > 1; } /*****************************************************************************/ bool dng_camera_profile::HasColorMatrix2 () const { return fColorMatrix2.Cols () == 3 && fColorMatrix2.Rows () == fColorMatrix1.Rows (); } /*****************************************************************************/ void dng_camera_profile::SetHueSatDeltas1 (const dng_hue_sat_map &deltas1) { fHueSatDeltas1 = deltas1; ClearFingerprint (); } /*****************************************************************************/ void dng_camera_profile::SetHueSatDeltas2 (const dng_hue_sat_map &deltas2) { fHueSatDeltas2 = deltas2; ClearFingerprint (); } /*****************************************************************************/ void dng_camera_profile::SetLookTable (const dng_hue_sat_map &table) { fLookTable = table; ClearFingerprint (); } /*****************************************************************************/ static void FingerprintMatrix (dng_md5_printer_stream &printer, const dng_matrix &matrix) { tag_matrix tag (0, matrix); // Tag's Put routine doesn't write the header, only the data tag.Put (printer); } /*****************************************************************************/ static void FingerprintHueSatMap (dng_md5_printer_stream &printer, const dng_hue_sat_map &map) { if (map.IsNull ()) return; uint32 hues; uint32 sats; uint32 vals; map.GetDivisions (hues, sats, vals); printer.Put_uint32 (hues); printer.Put_uint32 (sats); printer.Put_uint32 (vals); for (uint32 val = 0; val < vals; val++) for (uint32 hue = 0; hue < hues; hue++) for (uint32 sat = 0; sat < sats; sat++) { dng_hue_sat_map::HSBModify modify; map.GetDelta (hue, sat, val, modify); printer.Put_real32 (modify.fHueShift); printer.Put_real32 (modify.fSatScale); printer.Put_real32 (modify.fValScale); } } /*****************************************************************************/ void dng_camera_profile::CalculateFingerprint () const { DNG_ASSERT (!fWasStubbed, "CalculateFingerprint on stubbed profile"); dng_md5_printer_stream printer; // MD5 hash is always calculated on little endian data. printer.SetLittleEndian (); // The data that we fingerprint closely matches that saved // by the profile_tag_set class in dng_image_writer.cpp, with // the exception of the fingerprint itself. if (HasColorMatrix1 ()) { uint32 colorChannels = ColorMatrix1 ().Rows (); printer.Put_uint16 ((uint16) fCalibrationIlluminant1); FingerprintMatrix (printer, fColorMatrix1); if (fForwardMatrix1.Rows () == fColorMatrix1.Cols () && fForwardMatrix1.Cols () == fColorMatrix1.Rows ()) { FingerprintMatrix (printer, fForwardMatrix1); } if (colorChannels > 3 && fReductionMatrix1.Rows () * fReductionMatrix1.Cols () == colorChannels * 3) { FingerprintMatrix (printer, fReductionMatrix1); } if (HasColorMatrix2 ()) { printer.Put_uint16 ((uint16) fCalibrationIlluminant2); FingerprintMatrix (printer, fColorMatrix2); if (fForwardMatrix2.Rows () == fColorMatrix2.Cols () && fForwardMatrix2.Cols () == fColorMatrix2.Rows ()) { FingerprintMatrix (printer, fForwardMatrix2); } if (colorChannels > 3 && fReductionMatrix2.Rows () * fReductionMatrix2.Cols () == colorChannels * 3) { FingerprintMatrix (printer, fReductionMatrix2); } } printer.Put (fName.Get (), fName.Length ()); printer.Put (fProfileCalibrationSignature.Get (), fProfileCalibrationSignature.Length ()); printer.Put_uint32 (fEmbedPolicy); printer.Put (fCopyright.Get (), fCopyright.Length ()); bool haveHueSat1 = HueSatDeltas1 ().IsValid (); bool haveHueSat2 = HueSatDeltas2 ().IsValid () && HasColorMatrix2 (); if (haveHueSat1) { FingerprintHueSatMap (printer, fHueSatDeltas1); } if (haveHueSat2) { FingerprintHueSatMap (printer, fHueSatDeltas2); } if (haveHueSat1 || haveHueSat2) { if (fHueSatMapEncoding != 0) { printer.Put_uint32 (fHueSatMapEncoding); } } if (fLookTable.IsValid ()) { FingerprintHueSatMap (printer, fLookTable); if (fLookTableEncoding != 0) { printer.Put_uint32 (fLookTableEncoding); } } if (fBaselineExposureOffset.IsValid ()) { if (fBaselineExposureOffset.As_real64 () != 0.0) { printer.Put_real64 (fBaselineExposureOffset.As_real64 ()); } } if (fDefaultBlackRender != 0) { printer.Put_int32 (fDefaultBlackRender); } if (fToneCurve.IsValid ()) { for (uint32 i = 0; i < fToneCurve.fCoord.size (); i++) { printer.Put_real32 ((real32) fToneCurve.fCoord [i].h); printer.Put_real32 ((real32) fToneCurve.fCoord [i].v); } } } fFingerprint = printer.Result (); } /******************************************************************************/ bool dng_camera_profile::ValidForwardMatrix (const dng_matrix &m) { const real64 kThreshold = 0.01; if (m.NotEmpty ()) { dng_vector cameraOne; cameraOne.SetIdentity (m.Cols ()); dng_vector xyz = m * cameraOne; dng_vector pcs = PCStoXYZ (); if (Abs_real64 (xyz [0] - pcs [0]) > kThreshold || Abs_real64 (xyz [1] - pcs [1]) > kThreshold || Abs_real64 (xyz [2] - pcs [2]) > kThreshold) { return false; } } return true; } /******************************************************************************/ bool dng_camera_profile::IsValid (uint32 channels) const { // For Monochrome images, we ignore the camera profile. if (channels == 1) { return true; } // ColorMatrix1 is required for all color images. if (fColorMatrix1.Cols () != 3 || fColorMatrix1.Rows () != channels) { #if qDNGValidate ReportError ("ColorMatrix1 is wrong size"); #endif return false; } // ColorMatrix2 is optional, but it must be valid if present. if (fColorMatrix2.Cols () != 0 || fColorMatrix2.Rows () != 0) { if (fColorMatrix2.Cols () != 3 || fColorMatrix2.Rows () != channels) { #if qDNGValidate ReportError ("ColorMatrix2 is wrong size"); #endif return false; } } // ForwardMatrix1 is optional, but it must be valid if present. if (fForwardMatrix1.Cols () != 0 || fForwardMatrix1.Rows () != 0) { if (fForwardMatrix1.Rows () != 3 || fForwardMatrix1.Cols () != channels) { #if qDNGValidate ReportError ("ForwardMatrix1 is wrong size"); #endif return false; } // Make sure ForwardMatrix1 does a valid mapping. if (!ValidForwardMatrix (fForwardMatrix1)) { #if qDNGValidate ReportError ("ForwardMatrix1 does not map equal camera values to XYZ D50"); #endif return false; } } // ForwardMatrix2 is optional, but it must be valid if present. if (fForwardMatrix2.Cols () != 0 || fForwardMatrix2.Rows () != 0) { if (fForwardMatrix2.Rows () != 3 || fForwardMatrix2.Cols () != channels) { #if qDNGValidate ReportError ("ForwardMatrix2 is wrong size"); #endif return false; } // Make sure ForwardMatrix2 does a valid mapping. if (!ValidForwardMatrix (fForwardMatrix2)) { #if qDNGValidate ReportError ("ForwardMatrix2 does not map equal camera values to XYZ D50"); #endif return false; } } // ReductionMatrix1 is optional, but it must be valid if present. if (fReductionMatrix1.Cols () != 0 || fReductionMatrix1.Rows () != 0) { if (fReductionMatrix1.Cols () != channels || fReductionMatrix1.Rows () != 3) { #if qDNGValidate ReportError ("ReductionMatrix1 is wrong size"); #endif return false; } } // ReductionMatrix2 is optional, but it must be valid if present. if (fReductionMatrix2.Cols () != 0 || fReductionMatrix2.Rows () != 0) { if (fReductionMatrix2.Cols () != channels || fReductionMatrix2.Rows () != 3) { #if qDNGValidate ReportError ("ReductionMatrix2 is wrong size"); #endif return false; } } // Make sure ColorMatrix1 is invertable. try { if (fReductionMatrix1.NotEmpty ()) { (void) Invert (fColorMatrix1, fReductionMatrix1); } else { (void) Invert (fColorMatrix1); } } catch (...) { #if qDNGValidate ReportError ("ColorMatrix1 is not invertable"); #endif return false; } // Make sure ColorMatrix2 is invertable. if (fColorMatrix2.NotEmpty ()) { try { if (fReductionMatrix2.NotEmpty ()) { (void) Invert (fColorMatrix2, fReductionMatrix2); } else { (void) Invert (fColorMatrix2); } } catch (...) { #if qDNGValidate ReportError ("ColorMatrix2 is not invertable"); #endif return false; } } return true; } /*****************************************************************************/ bool dng_camera_profile::EqualData (const dng_camera_profile &profile) const { return fCalibrationIlluminant1 == profile.fCalibrationIlluminant1 && fCalibrationIlluminant2 == profile.fCalibrationIlluminant2 && fColorMatrix1 == profile.fColorMatrix1 && fColorMatrix2 == profile.fColorMatrix2 && fForwardMatrix1 == profile.fForwardMatrix1 && fForwardMatrix2 == profile.fForwardMatrix2 && fReductionMatrix1 == profile.fReductionMatrix1 && fReductionMatrix2 == profile.fReductionMatrix2 && fHueSatDeltas1 == profile.fHueSatDeltas1 && fHueSatDeltas2 == profile.fHueSatDeltas2 && fHueSatMapEncoding == profile.fHueSatMapEncoding && fLookTable == profile.fLookTable && fLookTableEncoding == profile.fLookTableEncoding && fDefaultBlackRender == profile.fDefaultBlackRender && fToneCurve == profile.fToneCurve && fBaselineExposureOffset.As_real64 () == profile.fBaselineExposureOffset.As_real64 () && fProfileCalibrationSignature == profile.fProfileCalibrationSignature; } /*****************************************************************************/ void dng_camera_profile::ReadHueSatMap (dng_stream &stream, dng_hue_sat_map &hueSatMap, uint32 hues, uint32 sats, uint32 vals, bool skipSat0) { hueSatMap.SetDivisions (hues, sats, vals); for (uint32 val = 0; val < vals; val++) { for (uint32 hue = 0; hue < hues; hue++) { for (uint32 sat = skipSat0 ? 1 : 0; sat < sats; sat++) { dng_hue_sat_map::HSBModify modify; modify.fHueShift = stream.Get_real32 (); modify.fSatScale = stream.Get_real32 (); modify.fValScale = stream.Get_real32 (); hueSatMap.SetDelta (hue, sat, val, modify); } } } } /*****************************************************************************/ void dng_camera_profile::Parse (dng_stream &stream, dng_camera_profile_info &profileInfo) { SetUniqueCameraModelRestriction (profileInfo.fUniqueCameraModel.Get ()); if (profileInfo.fProfileName.NotEmpty ()) { SetName (profileInfo.fProfileName.Get ()); } SetCopyright (profileInfo.fProfileCopyright.Get ()); SetEmbedPolicy (profileInfo.fEmbedPolicy); SetCalibrationIlluminant1 (profileInfo.fCalibrationIlluminant1); SetColorMatrix1 (profileInfo.fColorMatrix1); if (profileInfo.fForwardMatrix1.NotEmpty ()) { SetForwardMatrix1 (profileInfo.fForwardMatrix1); } if (profileInfo.fReductionMatrix1.NotEmpty ()) { SetReductionMatrix1 (profileInfo.fReductionMatrix1); } if (profileInfo.fColorMatrix2.NotEmpty ()) { SetCalibrationIlluminant2 (profileInfo.fCalibrationIlluminant2); SetColorMatrix2 (profileInfo.fColorMatrix2); if (profileInfo.fForwardMatrix2.NotEmpty ()) { SetForwardMatrix2 (profileInfo.fForwardMatrix2); } if (profileInfo.fReductionMatrix2.NotEmpty ()) { SetReductionMatrix2 (profileInfo.fReductionMatrix2); } } SetProfileCalibrationSignature (profileInfo.fProfileCalibrationSignature.Get ()); if (profileInfo.fHueSatDeltas1Offset != 0 && profileInfo.fHueSatDeltas1Count != 0) { TempBigEndian setEndianness (stream, profileInfo.fBigEndian); stream.SetReadPosition (profileInfo.fHueSatDeltas1Offset); bool skipSat0 = (profileInfo.fHueSatDeltas1Count == SafeUint32Mult( profileInfo.fProfileHues, SafeUint32Sub(profileInfo.fProfileSats, 1), profileInfo.fProfileVals, 3)); ReadHueSatMap (stream, fHueSatDeltas1, profileInfo.fProfileHues, profileInfo.fProfileSats, profileInfo.fProfileVals, skipSat0); } if (profileInfo.fHueSatDeltas2Offset != 0 && profileInfo.fHueSatDeltas2Count != 0) { TempBigEndian setEndianness (stream, profileInfo.fBigEndian); stream.SetReadPosition (profileInfo.fHueSatDeltas2Offset); bool skipSat0 = (profileInfo.fHueSatDeltas2Count == SafeUint32Mult( profileInfo.fProfileHues, SafeUint32Sub(profileInfo.fProfileSats, 1), profileInfo.fProfileVals, 3)); ReadHueSatMap (stream, fHueSatDeltas2, profileInfo.fProfileHues, profileInfo.fProfileSats, profileInfo.fProfileVals, skipSat0); } if (profileInfo.fLookTableOffset != 0 && profileInfo.fLookTableCount != 0) { TempBigEndian setEndianness (stream, profileInfo.fBigEndian); stream.SetReadPosition (profileInfo.fLookTableOffset); bool skipSat0 = (profileInfo.fLookTableCount == SafeUint32Mult( profileInfo.fLookTableHues, SafeUint32Sub(profileInfo.fLookTableSats, 1), profileInfo.fLookTableVals, 3)); ReadHueSatMap (stream, fLookTable, profileInfo.fLookTableHues, profileInfo.fLookTableSats, profileInfo.fLookTableVals, skipSat0); } if ((profileInfo.fToneCurveCount & 1) == 0) { TempBigEndian setEndianness (stream, profileInfo.fBigEndian); stream.SetReadPosition (profileInfo.fToneCurveOffset); uint32 points = profileInfo.fToneCurveCount / 2; fToneCurve.fCoord.resize (points); for (size_t i = 0; i < points; i++) { dng_point_real64 point; point.h = stream.Get_real32 (); point.v = stream.Get_real32 (); fToneCurve.fCoord [i] = point; } } SetHueSatMapEncoding (profileInfo.fHueSatMapEncoding); SetLookTableEncoding (profileInfo.fLookTableEncoding); SetBaselineExposureOffset (profileInfo.fBaselineExposureOffset.As_real64 ()); SetDefaultBlackRender (profileInfo.fDefaultBlackRender); } /*****************************************************************************/ bool dng_camera_profile::ParseExtended (dng_stream &stream) { try { dng_camera_profile_info profileInfo; if (!profileInfo.ParseExtended (stream)) { return false; } Parse (stream, profileInfo); return true; } catch (...) { // Eat parsing errors. } return false; } /*****************************************************************************/ void dng_camera_profile::SetFourColorBayer () { uint32 j; if (!IsValid (3)) { ThrowProgramError (); } if (fColorMatrix1.NotEmpty ()) { dng_matrix m (4, 3); for (j = 0; j < 3; j++) { m [0] [j] = fColorMatrix1 [0] [j]; m [1] [j] = fColorMatrix1 [1] [j]; m [2] [j] = fColorMatrix1 [2] [j]; m [3] [j] = fColorMatrix1 [1] [j]; } fColorMatrix1 = m; } if (fColorMatrix2.NotEmpty ()) { dng_matrix m (4, 3); for (j = 0; j < 3; j++) { m [0] [j] = fColorMatrix2 [0] [j]; m [1] [j] = fColorMatrix2 [1] [j]; m [2] [j] = fColorMatrix2 [2] [j]; m [3] [j] = fColorMatrix2 [1] [j]; } fColorMatrix2 = m; } fReductionMatrix1.Clear (); fReductionMatrix2.Clear (); fForwardMatrix1.Clear (); fForwardMatrix2.Clear (); } /*****************************************************************************/ dng_hue_sat_map * dng_camera_profile::HueSatMapForWhite (const dng_xy_coord &white) const { if (fHueSatDeltas1.IsValid ()) { // If we only have the first table, just use it for any color temperature. if (!fHueSatDeltas2.IsValid ()) { return new dng_hue_sat_map (fHueSatDeltas1); } // Else we need to interpolate based on color temperature. real64 temperature1 = CalibrationTemperature1 (); real64 temperature2 = CalibrationTemperature2 (); if (temperature1 <= 0.0 || temperature2 <= 0.0 || temperature1 == temperature2) { return new dng_hue_sat_map (fHueSatDeltas1); } bool reverseOrder = temperature1 > temperature2; if (reverseOrder) { real64 temp = temperature1; temperature1 = temperature2; temperature2 = temp; } // Convert to temperature/offset space. dng_temperature td (white); // Find fraction to weight the first calibration. real64 g; if (td.Temperature () <= temperature1) g = 1.0; else if (td.Temperature () >= temperature2) g = 0.0; else { real64 invT = 1.0 / td.Temperature (); g = (invT - (1.0 / temperature2)) / ((1.0 / temperature1) - (1.0 / temperature2)); } // Fix up if we swapped the order. if (reverseOrder) { g = 1.0 - g; } // Do the interpolation. return dng_hue_sat_map::Interpolate (HueSatDeltas1 (), HueSatDeltas2 (), g); } return NULL; } /*****************************************************************************/ void dng_camera_profile::Stub () { (void) Fingerprint (); dng_hue_sat_map nullTable; fHueSatDeltas1 = nullTable; fHueSatDeltas2 = nullTable; fLookTable = nullTable; fToneCurve.SetInvalid (); fWasStubbed = true; } /*****************************************************************************/ void SplitCameraProfileName (const dng_string &name, dng_string &baseName, int32 &version) { baseName = name; version = 0; uint32 len = baseName.Length (); if (len > 5 && baseName.EndsWith (" beta")) { baseName.Truncate (len - 5); version += -10; } else if (len > 7) { char lastChar = name.Get () [len - 1]; if (lastChar >= '0' && lastChar <= '9') { dng_string temp = name; temp.Truncate (len - 1); if (temp.EndsWith (" beta ")) { baseName.Truncate (len - 7); version += ((int32) (lastChar - '0')) - 10; } } } len = baseName.Length (); if (len > 3) { char lastChar = name.Get () [len - 1]; if (lastChar >= '0' && lastChar <= '9') { dng_string temp = name; temp.Truncate (len - 1); if (temp.EndsWith (" v")) { baseName.Truncate (len - 3); version += ((int32) (lastChar - '0')) * 100; } } } } /*****************************************************************************/ void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator, uint32 encoding, AutoPtr &encodeTable, AutoPtr &decodeTable, bool subSample) { encodeTable.Reset (); decodeTable.Reset (); switch (encoding) { case encoding_Linear: { break; } case encoding_sRGB: { encodeTable.Reset (new dng_1d_table); decodeTable.Reset (new dng_1d_table); const dng_1d_function & curve = dng_function_GammaEncode_sRGB::Get (); encodeTable->Initialize (allocator, curve, subSample); const dng_1d_inverse inverse (curve); decodeTable->Initialize (allocator, inverse, subSample); break; } default: { DNG_REPORT ("Unsupported hue sat map / look table encoding."); break; } } } /*****************************************************************************/