| /*****************************************************************************/ |
| // Copyright 2006-2023 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. |
| /*****************************************************************************/ |
| |
| #include "dng_camera_profile.h" |
| |
| #include "dng_1d_table.h" |
| #include "dng_assertions.h" |
| #include "dng_color_space.h" |
| #include "dng_gain_map.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_shared.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"; |
| |
| /*****************************************************************************/ |
| |
| const char * kProfileName_GroupPrefix = "Group: "; |
| |
| /*****************************************************************************/ |
| |
| bool HasProfileGroupPrefix (const dng_string &name) |
| { |
| |
| return name.StartsWith (kProfileName_GroupPrefix, true) && |
| name.Length () > strlen (kProfileName_GroupPrefix); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_string StripProfileGroupPrefix (const dng_string &name) |
| { |
| |
| if (HasProfileGroupPrefix (name)) |
| { |
| |
| dng_string result (name.Get () + strlen (kProfileName_GroupPrefix)); |
| |
| return result; |
| |
| } |
| |
| return name; |
| |
| } |
| |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| |
| void dng_camera_profile_id::AddDigest (dng_md5_printer &printer) const |
| { |
| |
| printer.Process ("DCPI", 4); |
| |
| if (Name ().NotEmpty ()) |
| { |
| |
| printer.Process (Name ().Get (), |
| Name ().Length ()); |
| |
| } |
| |
| if (Fingerprint ().IsValid ()) |
| { |
| |
| printer.Process (fFingerprint.data, |
| uint32 (sizeof (fFingerprint.data))); |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| /*****************************************************************************/ |
| |
| dng_camera_profile::dng_camera_profile () |
| |
| : fName () |
| , fCalibrationIlluminant1 (lsUnknown) |
| , fCalibrationIlluminant2 (lsUnknown) |
| , fCalibrationIlluminant3 (lsUnknown) |
| , fColorMatrix1 () |
| , fColorMatrix2 () |
| , fColorMatrix3 () |
| , fForwardMatrix1 () |
| , fForwardMatrix2 () |
| , fForwardMatrix3 () |
| , fReductionMatrix1 () |
| , fReductionMatrix2 () |
| , fReductionMatrix3 () |
| , fFingerprint () |
| , fRenderDataFingerprint () |
| , fCopyright () |
| , fEmbedPolicy (pepAllowCopying) |
| , fHueSatDeltas1 () |
| , fHueSatDeltas2 () |
| , fHueSatDeltas3 () |
| , fHueSatMapEncoding (encoding_Linear) |
| , fLookTable () |
| , fLookTableEncoding (encoding_Linear) |
| , fBaselineExposureOffset (0, 100) |
| , fDefaultBlackRender (defaultBlackRender_Auto) |
| , fToneCurve () |
| , fToneMethod (profileToneMethod_Unspecified) |
| , fProfileCalibrationSignature () |
| , fUniqueCameraModelRestriction () |
| , fWasReadFromDNG (false) |
| , fWasReadFromDisk (false) |
| , fWasStubbed (false) |
| |
| { |
| |
| fToneCurve.SetInvalid (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_camera_profile::~dng_camera_profile () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| uint32 dng_camera_profile::IlluminantModel () const |
| { |
| |
| // Start by assuming we have a single-illuminant model. |
| |
| uint32 model = 1; |
| |
| // If we have a 2nd calibration illuminant and 2nd color matrix, then |
| // assume we are using the dual-illuminnt model. |
| |
| if ((CalibrationIlluminant2 () != lsUnknown) && HasColorMatrix2 ()) |
| { |
| |
| model = 2; |
| |
| // If we also have a 3rd calibration illuminant and 3rd color matrix, |
| // then assume we are using the triple-illuminnt model. |
| |
| if ((CalibrationIlluminant3 () != lsUnknown) && HasColorMatrix3 ()) |
| { |
| |
| model = 3; |
| |
| } |
| |
| } |
| |
| return model; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| real64 dng_camera_profile::IlluminantToTemperature (uint32 light, |
| const dng_illuminant_data &data) |
| { |
| |
| 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; |
| } |
| |
| case lsOther: |
| { |
| return dng_temperature (data.WhiteXY ()).Temperature (); |
| } |
| |
| 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 (); |
| |
| } |
| |
| /******************************************************************************/ |
| |
| void dng_camera_profile::SetColorMatrix3 (const dng_matrix &m) |
| { |
| |
| fColorMatrix3 = m; |
| |
| NormalizeColorMatrix (fColorMatrix3); |
| |
| 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::SetForwardMatrix3 (const dng_matrix &m) |
| { |
| |
| fForwardMatrix3 = m; |
| |
| fForwardMatrix3.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 (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::SetReductionMatrix3 (const dng_matrix &m) |
| { |
| |
| fReductionMatrix3 = m; |
| |
| fReductionMatrix3.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 (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::HasColorMatrix3 () const |
| { |
| |
| return fColorMatrix3.Cols () == 3 && |
| fColorMatrix3.Rows () == fColorMatrix2.Rows () && |
| fColorMatrix3.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::SetHueSatDeltas3 (const dng_hue_sat_map &deltas3) |
| { |
| |
| fHueSatDeltas3 = deltas3; |
| |
| 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); |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_fingerprint dng_camera_profile::CalculateFingerprint (bool renderDataOnly) 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); |
| |
| } |
| |
| // Only include 2nd color matrix if we have 1st color matrix. |
| |
| 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); |
| |
| } |
| |
| // Only include 3rd color matrix if we have 2nd color matrix |
| // (which means we also have the 1st color matrix). |
| |
| if (HasColorMatrix3 ()) |
| { |
| |
| printer.Put_uint16 ((uint16) fCalibrationIlluminant3); |
| |
| FingerprintMatrix (printer, fColorMatrix3); |
| |
| if (fForwardMatrix3.Rows () == fColorMatrix3.Cols () && |
| fForwardMatrix3.Cols () == fColorMatrix3.Rows ()) |
| { |
| |
| FingerprintMatrix (printer, fForwardMatrix3); |
| |
| } |
| |
| if (colorChannels > 3 && fReductionMatrix3.Rows () * |
| fReductionMatrix3.Cols () == colorChannels * 3) |
| { |
| |
| FingerprintMatrix (printer, fReductionMatrix3); |
| |
| } |
| |
| } // has color matrix 3 |
| |
| } // has color matrix 2 |
| |
| if (!renderDataOnly) |
| { |
| |
| printer.Put (fName.Get (), |
| fName.Length ()); |
| |
| printer.Put (fGroupName.Get (), |
| fGroupName.Length ()); |
| |
| } |
| |
| printer.Put (fProfileCalibrationSignature.Get (), |
| fProfileCalibrationSignature.Length ()); |
| |
| if (!renderDataOnly) |
| { |
| |
| printer.Put_uint32 (fEmbedPolicy); |
| |
| printer.Put (fCopyright.Get (), |
| fCopyright.Length ()); |
| |
| } |
| |
| bool haveHueSat1 = HueSatDeltas1 ().IsValid (); |
| |
| bool haveHueSat2 = HueSatDeltas2 ().IsValid () && |
| HasColorMatrix2 (); |
| |
| bool haveHueSat3 = HueSatDeltas3 ().IsValid () && |
| HasColorMatrix3 (); |
| |
| if (haveHueSat1) |
| { |
| |
| FingerprintHueSatMap (printer, fHueSatDeltas1); |
| |
| } |
| |
| if (haveHueSat2) |
| { |
| |
| FingerprintHueSatMap (printer, fHueSatDeltas2); |
| |
| } |
| |
| if (haveHueSat3) |
| { |
| |
| FingerprintHueSatMap (printer, fHueSatDeltas3); |
| |
| } |
| |
| if (haveHueSat1 || haveHueSat2 || haveHueSat3) |
| { |
| |
| 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); |
| |
| } |
| |
| } |
| |
| if (fToneMethod != profileToneMethod_Unspecified) |
| { |
| |
| printer.Put_int32 (fToneMethod); |
| |
| } |
| |
| } |
| |
| // ProfileGainTableMap. |
| |
| { |
| |
| auto pgtm = ShareProfileGainTableMap (); |
| |
| if (pgtm) |
| { |
| |
| dng_fingerprint digest = pgtm->GetFingerprint (); |
| |
| printer.Put (digest.data, |
| uint32 (sizeof (digest.data))); |
| |
| } |
| |
| } |
| |
| // ProfileDynamicRange. |
| |
| { |
| |
| const auto &range = DynamicRangeInfo (); |
| |
| if (range.IsHDR ()) |
| { |
| |
| printer.Put ("hdr", 3); |
| |
| if (range.fHintMaxOutputValue != 1.0f) |
| printer.Put (&range.fHintMaxOutputValue, |
| uint32 (sizeof (range.fHintMaxOutputValue))); |
| |
| } |
| |
| } |
| |
| // RGBTables. |
| |
| if (HasMaskedRGBTables ()) |
| { |
| |
| dng_md5_printer rgbTablesPrinter; |
| |
| MaskedRGBTables ().AddDigest (rgbTablesPrinter); |
| |
| auto rgbTableDigest = rgbTablesPrinter.Result (); |
| |
| printer.Put (rgbTableDigest.data, |
| uint32 (sizeof (rgbTableDigest.data))); |
| |
| } |
| |
| return printer.Result (); |
| |
| } |
| |
| /******************************************************************************/ |
| |
| dng_fingerprint dng_camera_profile::UniqueID () const |
| { |
| |
| dng_md5_printer_stream printer; |
| |
| // MD5 hash is always calculated on little endian data. |
| |
| printer.SetLittleEndian (); |
| |
| // Start with the existing fingerprint. |
| |
| dng_fingerprint fingerprint = Fingerprint (); |
| |
| printer.Put (fingerprint.data, |
| (uint32) sizeof (fingerprint.data)); |
| |
| // Also include the UniqueCameraModelRestriction tag. |
| |
| printer.Put (fUniqueCameraModelRestriction.Get (), |
| fUniqueCameraModelRestriction.Length ()); |
| |
| // Add any other needed fields here. |
| |
| // ... |
| |
| return 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 |
| { |
| |
| bool hasFirstTwoColorMatrices = false; |
| |
| bool hasThreeColorMatrices = false; |
| |
| // 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; |
| |
| } |
| |
| // If we reached here, it means we have ColorMatrix1 and ColorMatrix2. |
| |
| hasFirstTwoColorMatrices = true; |
| |
| } |
| |
| // ColorMatrix3 is optional, but it must be valid if present. |
| |
| if (fColorMatrix3.Cols () != 0 || |
| fColorMatrix3.Rows () != 0) |
| { |
| |
| if (fColorMatrix3.Cols () != 3 || |
| fColorMatrix3.Rows () != channels) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix3 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Furthermore, the first two color matrices must also be present. |
| |
| if (!hasFirstTwoColorMatrices) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix3 present without ColorMatrix1/ColorMatrix2"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // If we reached here, it means we have all three color matrices defined. |
| |
| hasThreeColorMatrices = true; |
| |
| } |
| |
| // 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; |
| |
| } |
| |
| } |
| |
| // ForwardMatrix3 is optional, but it must be valid if present. |
| |
| if (fForwardMatrix3.Cols () != 0 || |
| fForwardMatrix3.Rows () != 0) |
| { |
| |
| if (fForwardMatrix3.Rows () != 3 || |
| fForwardMatrix3.Cols () != channels) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ForwardMatrix3 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Make sure ForwardMatrix3 does a valid mapping. |
| |
| if (!ValidForwardMatrix (fForwardMatrix3)) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ForwardMatrix3 does not map equal camera values to XYZ D50"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // ForwardMatrix3 is only allowed if we have three color matrices. |
| |
| if (!hasThreeColorMatrices) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ForwardMatrix3 present without three color matrices"); |
| |
| #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; |
| |
| } |
| |
| } |
| |
| // ReductionMatrix3 is optional, but it must be valid if present. |
| |
| if (fReductionMatrix3.Cols () != 0 || |
| fReductionMatrix3.Rows () != 0) |
| { |
| |
| if (fReductionMatrix3.Cols () != channels || |
| fReductionMatrix3.Rows () != 3) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ReductionMatrix3 is wrong size"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // ReductionMatrix3 is only allowed if we have three color matrices. |
| |
| if (!hasThreeColorMatrices) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ReductionMatrix3 present without three color matrices"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // Make sure ColorMatrix1 is invertible. |
| |
| try |
| { |
| |
| if (fReductionMatrix1.NotEmpty ()) |
| { |
| |
| (void) Invert (fColorMatrix1, |
| fReductionMatrix1); |
| |
| } |
| |
| else |
| { |
| |
| (void) Invert (fColorMatrix1); |
| |
| } |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix1 is not invertible"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // Make sure ColorMatrix2 is invertible. |
| |
| if (fColorMatrix2.NotEmpty ()) |
| { |
| |
| try |
| { |
| |
| if (fReductionMatrix2.NotEmpty ()) |
| { |
| |
| (void) Invert (fColorMatrix2, |
| fReductionMatrix2); |
| |
| } |
| |
| else |
| { |
| |
| (void) Invert (fColorMatrix2); |
| |
| } |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix2 is not invertible"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // Make sure ColorMatrix3 is invertible. |
| |
| if (fColorMatrix3.NotEmpty ()) |
| { |
| |
| try |
| { |
| |
| if (fReductionMatrix3.NotEmpty ()) |
| { |
| |
| (void) Invert (fColorMatrix3, |
| fReductionMatrix3); |
| |
| } |
| |
| else |
| { |
| |
| (void) Invert (fColorMatrix3); |
| |
| } |
| |
| } |
| |
| catch (...) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix3 is not invertible"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // If this is a triple-illuminant profile, then we have some extra |
| // requirements. |
| |
| if (IlluminantModel () == 3) |
| { |
| |
| // None of the illuminants can be unknown. |
| |
| if (CalibrationIlluminant1 () == lsUnknown || |
| CalibrationIlluminant2 () == lsUnknown || |
| CalibrationIlluminant3 () == lsUnknown) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("CalibrationIlluminant1/2/3 cannot be unknown for " |
| "a triple-illuminant profile"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // All of the illuminants must be distinct. |
| |
| dng_illuminant_data light1 (CalibrationIlluminant1 (), &IlluminantData1 ()); |
| dng_illuminant_data light2 (CalibrationIlluminant2 (), &IlluminantData2 ()); |
| dng_illuminant_data light3 (CalibrationIlluminant3 (), &IlluminantData3 ()); |
| |
| dng_xy_coord white1 = light1.WhiteXY (); |
| dng_xy_coord white2 = light2.WhiteXY (); |
| dng_xy_coord white3 = light3.WhiteXY (); |
| |
| if (white1 == white2 || |
| white1 == white3 || |
| white2 == white3) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("In a triple-illuminant profile all three illuminants " |
| "must be distinct"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // We must have all three color matrices. |
| |
| if (!HasColorMatrix1 () || |
| !HasColorMatrix2 () || |
| !HasColorMatrix3 ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("ColorMatrix1/2/3 must all be present and valid for " |
| "a triple-illuminant profile"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| // ForwardMatrix must be present for all or absent for all. |
| |
| if (ForwardMatrix1 ().NotEmpty () || |
| ForwardMatrix2 ().NotEmpty () || |
| ForwardMatrix3 ().NotEmpty ()) |
| { |
| |
| if (ForwardMatrix1 ().IsEmpty () || |
| ForwardMatrix2 ().IsEmpty () || |
| ForwardMatrix3 ().IsEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("For a triple-illuminant profile, ForwardMatrix " |
| "must be absent for all three illuminants, or " |
| "present for all three illuminants"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // ReductionMatrix must be present for all or absent for all. |
| |
| if (ReductionMatrix1 ().NotEmpty () || |
| ReductionMatrix2 ().NotEmpty () || |
| ReductionMatrix3 ().NotEmpty ()) |
| { |
| |
| if (ReductionMatrix1 ().IsEmpty () || |
| ReductionMatrix2 ().IsEmpty () || |
| ReductionMatrix3 ().IsEmpty ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("For a triple-illuminant profile, ReductionMatrix " |
| "must be absent for all three illuminants, or " |
| "present for all three illuminants"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| // Hue sat map must be present for all or absent for all. |
| |
| if (HueSatDeltas1 ().IsValid () || |
| HueSatDeltas2 ().IsValid () || |
| HueSatDeltas3 ().IsValid ()) |
| { |
| |
| if (HueSatDeltas1 ().IsNull () || |
| HueSatDeltas2 ().IsNull () || |
| HueSatDeltas3 ().IsNull ()) |
| { |
| |
| #if qDNGValidate |
| |
| ReportError ("For a triple-illuminant profile, HueSatDeltas " |
| "must be absent for all three illuminants, or " |
| "present for all three illuminants"); |
| |
| #endif |
| |
| return false; |
| |
| } |
| |
| } |
| |
| } |
| |
| return true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| 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); |
| |
| } |
| |
| } |
| |
| } |
| |
| hueSatMap.AssignNewUniqueRuntimeFingerprint (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| 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 (CalibrationIlluminant1 () == lsOther) |
| { |
| |
| SetIlluminantData1 (profileInfo.fIlluminantData1); |
| |
| } |
| |
| // Deal with 2nd illuminant. |
| |
| if (profileInfo.fColorMatrix2.NotEmpty ()) |
| { |
| |
| SetCalibrationIlluminant2 (profileInfo.fCalibrationIlluminant2); |
| |
| SetColorMatrix2 (profileInfo.fColorMatrix2); |
| |
| if (profileInfo.fForwardMatrix2.NotEmpty ()) |
| { |
| |
| SetForwardMatrix2 (profileInfo.fForwardMatrix2); |
| |
| } |
| |
| if (profileInfo.fReductionMatrix2.NotEmpty ()) |
| { |
| |
| SetReductionMatrix2 (profileInfo.fReductionMatrix2); |
| |
| } |
| |
| if (CalibrationIlluminant2 () == lsOther) |
| { |
| |
| SetIlluminantData2 (profileInfo.fIlluminantData2); |
| |
| } |
| |
| } |
| |
| // Deal with 3rd illuminant. |
| |
| if (profileInfo.fColorMatrix3.NotEmpty ()) |
| { |
| |
| SetCalibrationIlluminant3 (profileInfo.fCalibrationIlluminant3); |
| |
| SetColorMatrix3 (profileInfo.fColorMatrix3); |
| |
| if (profileInfo.fForwardMatrix3.NotEmpty ()) |
| { |
| |
| SetForwardMatrix3 (profileInfo.fForwardMatrix3); |
| |
| } |
| |
| if (profileInfo.fReductionMatrix3.NotEmpty ()) |
| { |
| |
| SetReductionMatrix3 (profileInfo.fReductionMatrix3); |
| |
| } |
| |
| if (CalibrationIlluminant3 () == lsOther) |
| { |
| |
| SetIlluminantData3 (profileInfo.fIlluminantData3); |
| |
| } |
| |
| } |
| |
| 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.fHueSatDeltas3Offset != 0 && |
| profileInfo.fHueSatDeltas3Count != 0) |
| { |
| |
| TempBigEndian setEndianness (stream, profileInfo.fBigEndian); |
| |
| stream.SetReadPosition (profileInfo.fHueSatDeltas3Offset); |
| |
| bool skipSat0 = (profileInfo.fHueSatDeltas3Count == profileInfo.fProfileHues * |
| (profileInfo.fProfileSats - 1) * |
| profileInfo.fProfileVals * 3); |
| |
| ReadHueSatMap (stream, |
| fHueSatDeltas3, |
| 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; |
| |
| if (points > kMaxToneCurvePoints) |
| { |
| ThrowProgramError ("Too many tone curve points"); |
| } |
| |
| 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; |
| |
| } |
| |
| } |
| |
| SetToneMethod (profileInfo.fToneMethod); |
| |
| SetHueSatMapEncoding (profileInfo.fHueSatMapEncoding); |
| |
| SetLookTableEncoding (profileInfo.fLookTableEncoding); |
| |
| SetBaselineExposureOffset (profileInfo.fBaselineExposureOffset.As_real64 ()); |
| |
| SetDefaultBlackRender (profileInfo.fDefaultBlackRender); |
| |
| SetProfileGainTableMap (profileInfo.fProfileGainTableMap); |
| |
| SetGroupName (profileInfo.fProfileGroupName); |
| |
| SetDynamicRangeInfo (profileInfo.fProfileDynamicRange); |
| |
| SetMaskedRGBTables (profileInfo.fMaskedRGBTables); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| 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; |
| |
| } |
| |
| if (fColorMatrix3.NotEmpty ()) |
| { |
| |
| dng_matrix m (4, 3); |
| |
| for (j = 0; j < 3; j++) |
| { |
| m [0] [j] = fColorMatrix3 [0] [j]; |
| m [1] [j] = fColorMatrix3 [1] [j]; |
| m [2] [j] = fColorMatrix3 [2] [j]; |
| m [3] [j] = fColorMatrix3 [1] [j]; |
| } |
| |
| fColorMatrix3 = m; |
| |
| } |
| |
| fReductionMatrix1.Clear (); |
| fReductionMatrix2.Clear (); |
| fReductionMatrix3.Clear (); |
| |
| fForwardMatrix1.Clear (); |
| fForwardMatrix2.Clear (); |
| fForwardMatrix3.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); |
| |
| } |
| |
| // We have table 1 and table 2. |
| |
| if (IlluminantModel () == 3) |
| { |
| |
| // This means we also have a 3rd hue sat map, since all |
| // three are required to be present or absent for a |
| // triple-illuminant profile. |
| |
| return HueSatMapForWhite_Triple (white); |
| |
| } |
| |
| else |
| { |
| |
| // Dual-illuminant model. |
| |
| return HueSatMapForWhite_Dual (white); |
| |
| } |
| |
| } |
| |
| return nullptr; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_hue_sat_map * |
| dng_camera_profile::HueSatMapForWhite_Dual (const dng_xy_coord &white) const |
| { |
| |
| DNG_REQUIRE (fHueSatDeltas1.IsValid () && |
| fHueSatDeltas2.IsValid (), |
| "Bad hue sat map deltas 1 or 2"); |
| |
| // 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); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_hue_sat_map * |
| dng_camera_profile::HueSatMapForWhite_Triple (const dng_xy_coord &white) const |
| { |
| |
| DNG_REQUIRE (fHueSatDeltas1.IsValid () && |
| fHueSatDeltas2.IsValid () && |
| fHueSatDeltas3.IsValid (), |
| "Bad hue sat map deltas 1 or 2 or 3"); |
| |
| real64 w1, w2, w3; |
| |
| CalculateTripleIlluminantWeights |
| (white, |
| dng_illuminant_data (CalibrationIlluminant1 (), &IlluminantData1 ()), |
| dng_illuminant_data (CalibrationIlluminant2 (), &IlluminantData2 ()), |
| dng_illuminant_data (CalibrationIlluminant3 (), &IlluminantData3 ()), |
| w1, |
| w2, |
| w3); |
| |
| return dng_hue_sat_map::Interpolate (HueSatDeltas1 (), |
| HueSatDeltas2 (), |
| HueSatDeltas3 (), |
| w1, |
| w2); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::Stub () |
| { |
| |
| (void) Fingerprint (); |
| |
| (void) RenderDataFingerprint (); |
| |
| dng_hue_sat_map nullTable; |
| |
| fHueSatDeltas1 = nullTable; |
| fHueSatDeltas2 = nullTable; |
| fHueSatDeltas3 = nullTable; |
| |
| fLookTable = nullTable; |
| |
| fToneCurve.SetInvalid (); |
| |
| fWasStubbed = true; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::HasProfileGainTableMap () const |
| { |
| |
| return fProfileGainTableMap != nullptr; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::SetProfileGainTableMap |
| (const std::shared_ptr<const dng_gain_table_map> &gainTableMap) |
| { |
| |
| fProfileGainTableMap = gainTableMap; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| const dng_camera_profile_dynamic_range & dng_camera_profile::DynamicRangeInfo () const |
| { |
| |
| if (fDynamicRangeInfo) |
| return *fDynamicRangeInfo; |
| |
| static const dng_camera_profile_dynamic_range sNoInfo; |
| |
| return sNoInfo; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::IsSDR () const |
| { |
| |
| return !IsHDR (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::IsHDR () const |
| { |
| |
| return DynamicRangeInfo ().IsHDR (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::SetDynamicRangeInfo (const dng_camera_profile_dynamic_range &info) |
| { |
| |
| fDynamicRangeInfo.reset (new dng_camera_profile_dynamic_range (info)); |
| |
| ClearFingerprint (); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::HasMaskedRGBTables () const |
| { |
| |
| return fMaskedRGBTables != nullptr; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| const dng_masked_rgb_tables & dng_camera_profile::MaskedRGBTables () const |
| { |
| |
| DNG_REQUIRE (HasMaskedRGBTables (), "Missing masked RGBTables"); |
| |
| return *fMaskedRGBTables; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::SetMaskedRGBTables |
| (const std::shared_ptr<const dng_masked_rgb_tables> &maskedRGBTables) |
| { |
| |
| fMaskedRGBTables = maskedRGBTables; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_camera_profile::SetMaskedRGBTables |
| (AutoPtr<dng_masked_rgb_tables> &maskedRGBTables) |
| { |
| |
| fMaskedRGBTables.reset (maskedRGBTables.Release ()); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::Uses_1_6_Features () const |
| { |
| |
| // If we require a DNG 1.6 reader, then we're obviously using 1.6 features. |
| |
| if (Requires_1_6_Reader ()) |
| { |
| return true; |
| } |
| |
| // Is this a triple-illuminant model? |
| |
| if (IlluminantModel () == 3) |
| { |
| return true; |
| } |
| |
| // Don't bother checking the individual tags like ForwardMatrix3. If we |
| // consider it a triple-illuminant profile, then we'll already have returned |
| // true -- see above. If we don't, then the extra tags don't matter. |
| |
| return false; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| bool dng_camera_profile::Requires_1_6_Reader () const |
| { |
| |
| // The only change in DNG 1.6 that breaks compatibility with older readers |
| // is the ability to specify custom data for illuminants 1 and 2. |
| |
| if (CalibrationIlluminant1 () == lsOther && |
| IlluminantData1 ().WhiteXY ().IsValid ()) |
| { |
| return true; |
| } |
| |
| if (CalibrationIlluminant2 () == lsOther && |
| IlluminantData2 ().WhiteXY ().IsValid ()) |
| { |
| return true; |
| } |
| |
| return false; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile::Uses_1_7_Features () const |
| { |
| |
| // Do we have a ProfileGainTableMap? |
| |
| if (HasProfileGainTableMap ()) |
| return true; |
| |
| // Do we have RGBTables? |
| |
| if (HasMaskedRGBTables ()) |
| return true; |
| |
| // Is this a HDR profile? |
| |
| if (DynamicRangeInfo ().IsValid () && |
| DynamicRangeInfo ().IsHDR ()) |
| return true; |
| |
| return false; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| dng_camera_profile_metadata::dng_camera_profile_metadata |
| (const dng_camera_profile &profile, |
| int32 index) |
| |
| : fProfileID (profile.ProfileID ()) |
| |
| , fGroupName (profile.GroupName ()) |
| |
| , fHDR (profile.DynamicRangeInfo ().IsHDR ()) |
| |
| , fRenderDataFingerprint (profile.RenderDataFingerprint ()) |
| |
| , fIsLegalToEmbed (profile.IsLegalToEmbed ()) |
| |
| , fWasReadFromDNG (profile.WasReadFromDNG ()) |
| |
| , fWasReadFromDisk (profile.WasReadFromDisk ()) |
| |
| , fUniqueID () |
| |
| , fFilePath () |
| |
| , fReadOnly (true) |
| |
| , fIndex (index) |
| |
| { |
| |
| if (fWasReadFromDisk) |
| { |
| fUniqueID = profile.UniqueID (); |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| bool dng_camera_profile_metadata::operator== |
| (const dng_camera_profile_metadata &metadata) const |
| { |
| |
| return fProfileID == metadata.fProfileID && |
| fGroupName == metadata.fGroupName && |
| fHDR == metadata.fHDR && |
| fRenderDataFingerprint == metadata.fRenderDataFingerprint && |
| fIsLegalToEmbed == metadata.fIsLegalToEmbed && |
| fWasReadFromDNG == metadata.fWasReadFromDNG && |
| fWasReadFromDisk == metadata.fWasReadFromDisk && |
| fUniqueID == metadata.fUniqueID && |
| fFilePath == metadata.fFilePath && |
| fReadOnly == metadata.fReadOnly && |
| fIndex == metadata.fIndex; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void SplitCameraProfileName (const dng_string &name, |
| dng_string &baseName, |
| int32 &version) |
| { |
| |
| baseName = name; |
| |
| version = 0; |
| |
| uint32 len = baseName.Length (); |
| |
| if (len == 7 && baseName.StartsWith ("ACR ", true)) |
| { |
| |
| if (name.Get () [len - 3] >= '0' && |
| name.Get () [len - 3] <= '9' && |
| name.Get () [len - 2] == '.' && |
| name.Get () [len - 1] >= '0' && |
| name.Get () [len - 1] <= '9') |
| |
| baseName.Truncate (3); |
| |
| version = ((int32) (name.Get () [len - 3] - '0')) * 10 + |
| ((int32) (name.Get () [len - 1] - '0')); |
| |
| return; |
| |
| } |
| |
| 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<dng_1d_table> &encodeTable, |
| AutoPtr<dng_1d_table> &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; |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |