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.
1385 lines
26 KiB
1385 lines
26 KiB
/*****************************************************************************/ |
|
// Copyright 2006-2008 Adobe Systems Incorporated |
|
// All Rights Reserved. |
|
// |
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in |
|
// accordance with the terms of the Adobe license agreement accompanying it. |
|
/*****************************************************************************/ |
|
|
|
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_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<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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
/*****************************************************************************/
|
|
|