Home | History | Annotate | Download | only in source
      1 /*****************************************************************************/
      2 // Copyright 2006-2008 Adobe Systems Incorporated
      3 // All Rights Reserved.
      4 //
      5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
      6 // accordance with the terms of the Adobe license agreement accompanying it.
      7 /*****************************************************************************/
      8 
      9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_camera_profile.cpp#1 $ */
     10 /* $DateTime: 2012/05/30 13:28:51 $ */
     11 /* $Change: 832332 $ */
     12 /* $Author: tknoll $ */
     13 
     14 #include "dng_camera_profile.h"
     15 
     16 #include "dng_1d_table.h"
     17 #include "dng_assertions.h"
     18 #include "dng_color_space.h"
     19 #include "dng_host.h"
     20 #include "dng_exceptions.h"
     21 #include "dng_image_writer.h"
     22 #include "dng_info.h"
     23 #include "dng_parse_utils.h"
     24 #include "dng_safe_arithmetic.h"
     25 #include "dng_tag_codes.h"
     26 #include "dng_tag_types.h"
     27 #include "dng_temperature.h"
     28 #include "dng_xy_coord.h"
     29 
     30 /*****************************************************************************/
     31 
     32 const char * kProfileName_Embedded = "Embedded";
     33 
     34 const char * kAdobeCalibrationSignature = "com.adobe";
     35 
     36 /*****************************************************************************/
     37 
     38 dng_camera_profile::dng_camera_profile ()
     39 
     40 	:	fName ()
     41 	,	fCalibrationIlluminant1 (lsUnknown)
     42 	,	fCalibrationIlluminant2 (lsUnknown)
     43 	,	fColorMatrix1 ()
     44 	,	fColorMatrix2 ()
     45 	,	fForwardMatrix1 ()
     46 	,	fForwardMatrix2 ()
     47 	,	fReductionMatrix1 ()
     48 	,	fReductionMatrix2 ()
     49 	,	fFingerprint ()
     50 	,	fCopyright ()
     51 	,	fEmbedPolicy (pepAllowCopying)
     52 	,	fHueSatDeltas1 ()
     53 	,	fHueSatDeltas2 ()
     54 	,	fHueSatMapEncoding (encoding_Linear)
     55 	,	fLookTable ()
     56 	,	fLookTableEncoding (encoding_Linear)
     57 	,	fBaselineExposureOffset (0, 100)
     58 	,	fDefaultBlackRender (defaultBlackRender_Auto)
     59 	,	fToneCurve ()
     60 	,	fProfileCalibrationSignature ()
     61 	,	fUniqueCameraModelRestriction ()
     62 	,	fWasReadFromDNG (false)
     63 	,	fWasReadFromDisk (false)
     64 	,	fWasBuiltinMatrix (false)
     65 	,	fWasStubbed (false)
     66 
     67 	{
     68 
     69 	fToneCurve.SetInvalid ();
     70 
     71 	}
     72 
     73 /*****************************************************************************/
     74 
     75 dng_camera_profile::~dng_camera_profile ()
     76 	{
     77 
     78 	}
     79 
     80 /*****************************************************************************/
     81 
     82 real64 dng_camera_profile::IlluminantToTemperature (uint32 light)
     83 	{
     84 
     85 	switch (light)
     86 		{
     87 
     88 		case lsStandardLightA:
     89 		case lsTungsten:
     90 			{
     91 			return 2850.0;
     92 			}
     93 
     94 		case lsISOStudioTungsten:
     95 			{
     96 			return 3200.0;
     97 			}
     98 
     99 		case lsD50:
    100 			{
    101 			return 5000.0;
    102 			}
    103 
    104 		case lsD55:
    105 		case lsDaylight:
    106 		case lsFineWeather:
    107 		case lsFlash:
    108 		case lsStandardLightB:
    109 			{
    110 			return 5500.0;
    111 			}
    112 
    113 		case lsD65:
    114 		case lsStandardLightC:
    115 		case lsCloudyWeather:
    116 			{
    117 			return 6500.0;
    118 			}
    119 
    120 		case lsD75:
    121 		case lsShade:
    122 			{
    123 			return 7500.0;
    124 			}
    125 
    126 		case lsDaylightFluorescent:
    127 			{
    128 			return (5700.0 + 7100.0) * 0.5;
    129 			}
    130 
    131 		case lsDayWhiteFluorescent:
    132 			{
    133 			return (4600.0 + 5500.0) * 0.5;
    134 			}
    135 
    136 		case lsCoolWhiteFluorescent:
    137 		case lsFluorescent:
    138 			{
    139 			return (3800.0 + 4500.0) * 0.5;
    140 			}
    141 
    142 		case lsWhiteFluorescent:
    143 			{
    144 			return (3250.0 + 3800.0) * 0.5;
    145 			}
    146 
    147 		case lsWarmWhiteFluorescent:
    148 			{
    149 			return (2600.0 + 3250.0) * 0.5;
    150 			}
    151 
    152 		default:
    153 			{
    154 			return 0.0;
    155 			}
    156 
    157 		}
    158 
    159 	}
    160 
    161 /******************************************************************************/
    162 
    163 void dng_camera_profile::NormalizeColorMatrix (dng_matrix &m)
    164 	{
    165 
    166 	if (m.NotEmpty ())
    167 		{
    168 
    169 		// Find scale factor to normalize the matrix.
    170 
    171 		dng_vector coord = m * PCStoXYZ ();
    172 
    173 		real64 maxCoord = coord.MaxEntry ();
    174 
    175 		if (maxCoord > 0.0 && (maxCoord < 0.99 || maxCoord > 1.01))
    176 			{
    177 
    178 			m.Scale (1.0 / maxCoord);
    179 
    180 			}
    181 
    182 		// Round to four decimal places.
    183 
    184 		m.Round (10000);
    185 
    186 		}
    187 
    188 	}
    189 
    190 /******************************************************************************/
    191 
    192 void dng_camera_profile::SetColorMatrix1 (const dng_matrix &m)
    193 	{
    194 
    195 	fColorMatrix1 = m;
    196 
    197 	NormalizeColorMatrix (fColorMatrix1);
    198 
    199 	ClearFingerprint ();
    200 
    201 	}
    202 
    203 /******************************************************************************/
    204 
    205 void dng_camera_profile::SetColorMatrix2 (const dng_matrix &m)
    206 	{
    207 
    208 	fColorMatrix2 = m;
    209 
    210 	NormalizeColorMatrix (fColorMatrix2);
    211 
    212 	ClearFingerprint ();
    213 
    214 	}
    215 
    216 /******************************************************************************/
    217 
    218 // Make sure the forward matrix maps to exactly the PCS.
    219 
    220 void dng_camera_profile::NormalizeForwardMatrix (dng_matrix &m)
    221 	{
    222 
    223 	if (m.NotEmpty ())
    224 		{
    225 
    226 		dng_vector cameraOne;
    227 
    228 		cameraOne.SetIdentity (m.Cols ());
    229 
    230 		dng_vector xyz = m * cameraOne;
    231 
    232 		m = PCStoXYZ ().AsDiagonal () *
    233 			Invert (xyz.AsDiagonal ()) *
    234 			m;
    235 
    236 		}
    237 
    238 	}
    239 
    240 /******************************************************************************/
    241 
    242 void dng_camera_profile::SetForwardMatrix1 (const dng_matrix &m)
    243 	{
    244 
    245 	fForwardMatrix1 = m;
    246 
    247 	fForwardMatrix1.Round (10000);
    248 
    249 	ClearFingerprint ();
    250 
    251 	}
    252 
    253 /******************************************************************************/
    254 
    255 void dng_camera_profile::SetForwardMatrix2 (const dng_matrix &m)
    256 	{
    257 
    258 	fForwardMatrix2 = m;
    259 
    260 	fForwardMatrix2.Round (10000);
    261 
    262 	ClearFingerprint ();
    263 
    264 	}
    265 
    266 /*****************************************************************************/
    267 
    268 void dng_camera_profile::SetReductionMatrix1 (const dng_matrix &m)
    269 	{
    270 
    271 	fReductionMatrix1 = m;
    272 
    273 	fReductionMatrix1.Round (10000);
    274 
    275 	ClearFingerprint ();
    276 
    277 	}
    278 
    279 /******************************************************************************/
    280 
    281 void dng_camera_profile::SetReductionMatrix2 (const dng_matrix &m)
    282 	{
    283 
    284 	fReductionMatrix2 = m;
    285 
    286 	fReductionMatrix2.Round (10000);
    287 
    288 	ClearFingerprint ();
    289 
    290 	}
    291 
    292 /*****************************************************************************/
    293 
    294 bool dng_camera_profile::HasColorMatrix1 () const
    295 	{
    296 
    297 	return fColorMatrix1.Cols () == 3 &&
    298 		   fColorMatrix1.Rows ()  > 1;
    299 
    300 	}
    301 
    302 /*****************************************************************************/
    303 
    304 bool dng_camera_profile::HasColorMatrix2 () const
    305 	{
    306 
    307 	return fColorMatrix2.Cols () == 3 &&
    308 		   fColorMatrix2.Rows () == fColorMatrix1.Rows ();
    309 
    310 	}
    311 
    312 /*****************************************************************************/
    313 
    314 void dng_camera_profile::SetHueSatDeltas1 (const dng_hue_sat_map &deltas1)
    315 	{
    316 
    317 	fHueSatDeltas1 = deltas1;
    318 
    319 	ClearFingerprint ();
    320 
    321 	}
    322 
    323 /*****************************************************************************/
    324 
    325 void dng_camera_profile::SetHueSatDeltas2 (const dng_hue_sat_map &deltas2)
    326 	{
    327 
    328 	fHueSatDeltas2 = deltas2;
    329 
    330 	ClearFingerprint ();
    331 
    332 	}
    333 
    334 /*****************************************************************************/
    335 
    336 void dng_camera_profile::SetLookTable (const dng_hue_sat_map &table)
    337 	{
    338 
    339 	fLookTable = table;
    340 
    341 	ClearFingerprint ();
    342 
    343 	}
    344 
    345 /*****************************************************************************/
    346 
    347 static void FingerprintMatrix (dng_md5_printer_stream &printer,
    348 							   const dng_matrix &matrix)
    349 	{
    350 
    351 	tag_matrix tag (0, matrix);
    352 
    353 	// Tag's Put routine doesn't write the header, only the data
    354 
    355 	tag.Put (printer);
    356 
    357 	}
    358 
    359 /*****************************************************************************/
    360 
    361 static void FingerprintHueSatMap (dng_md5_printer_stream &printer,
    362 								  const dng_hue_sat_map &map)
    363 	{
    364 
    365 	if (map.IsNull ())
    366 		return;
    367 
    368 	uint32 hues;
    369 	uint32 sats;
    370 	uint32 vals;
    371 
    372 	map.GetDivisions (hues, sats, vals);
    373 
    374 	printer.Put_uint32 (hues);
    375 	printer.Put_uint32 (sats);
    376 	printer.Put_uint32 (vals);
    377 
    378 	for (uint32 val = 0; val < vals; val++)
    379 		for (uint32 hue = 0; hue < hues; hue++)
    380 			for (uint32 sat = 0; sat < sats; sat++)
    381 				{
    382 
    383 				dng_hue_sat_map::HSBModify modify;
    384 
    385 				map.GetDelta (hue, sat, val, modify);
    386 
    387 				printer.Put_real32 (modify.fHueShift);
    388 				printer.Put_real32 (modify.fSatScale);
    389 				printer.Put_real32 (modify.fValScale);
    390 
    391 				}
    392 
    393 	}
    394 
    395 /*****************************************************************************/
    396 
    397 void dng_camera_profile::CalculateFingerprint () const
    398 	{
    399 
    400 	DNG_ASSERT (!fWasStubbed, "CalculateFingerprint on stubbed profile");
    401 
    402 	dng_md5_printer_stream printer;
    403 
    404 	// MD5 hash is always calculated on little endian data.
    405 
    406 	printer.SetLittleEndian ();
    407 
    408 	// The data that we fingerprint closely matches that saved
    409 	// by the profile_tag_set class in dng_image_writer.cpp, with
    410 	// the exception of the fingerprint itself.
    411 
    412 	if (HasColorMatrix1 ())
    413 		{
    414 
    415 		uint32 colorChannels = ColorMatrix1 ().Rows ();
    416 
    417 		printer.Put_uint16 ((uint16) fCalibrationIlluminant1);
    418 
    419 		FingerprintMatrix (printer, fColorMatrix1);
    420 
    421 		if (fForwardMatrix1.Rows () == fColorMatrix1.Cols () &&
    422 			fForwardMatrix1.Cols () == fColorMatrix1.Rows ())
    423 			{
    424 
    425 			FingerprintMatrix (printer, fForwardMatrix1);
    426 
    427 			}
    428 
    429 		if (colorChannels > 3 && fReductionMatrix1.Rows () *
    430 								 fReductionMatrix1.Cols () == colorChannels * 3)
    431 			{
    432 
    433 			FingerprintMatrix (printer, fReductionMatrix1);
    434 
    435 			}
    436 
    437 		if (HasColorMatrix2 ())
    438 			{
    439 
    440 			printer.Put_uint16 ((uint16) fCalibrationIlluminant2);
    441 
    442 			FingerprintMatrix (printer, fColorMatrix2);
    443 
    444 			if (fForwardMatrix2.Rows () == fColorMatrix2.Cols () &&
    445 				fForwardMatrix2.Cols () == fColorMatrix2.Rows ())
    446 				{
    447 
    448 				FingerprintMatrix (printer, fForwardMatrix2);
    449 
    450 				}
    451 
    452 			if (colorChannels > 3 && fReductionMatrix2.Rows () *
    453 									 fReductionMatrix2.Cols () == colorChannels * 3)
    454 				{
    455 
    456 				FingerprintMatrix (printer, fReductionMatrix2);
    457 
    458 				}
    459 
    460 			}
    461 
    462 		printer.Put (fName.Get    (),
    463 					 fName.Length ());
    464 
    465 		printer.Put (fProfileCalibrationSignature.Get    (),
    466 					 fProfileCalibrationSignature.Length ());
    467 
    468 		printer.Put_uint32 (fEmbedPolicy);
    469 
    470 		printer.Put (fCopyright.Get    (),
    471 					 fCopyright.Length ());
    472 
    473 		bool haveHueSat1 = HueSatDeltas1 ().IsValid ();
    474 
    475 		bool haveHueSat2 = HueSatDeltas2 ().IsValid () &&
    476 						   HasColorMatrix2 ();
    477 
    478 		if (haveHueSat1)
    479 			{
    480 
    481 			FingerprintHueSatMap (printer, fHueSatDeltas1);
    482 
    483 			}
    484 
    485 		if (haveHueSat2)
    486 			{
    487 
    488 			FingerprintHueSatMap (printer, fHueSatDeltas2);
    489 
    490 			}
    491 
    492 		if (haveHueSat1 || haveHueSat2)
    493 			{
    494 
    495 			if (fHueSatMapEncoding != 0)
    496 				{
    497 
    498 				printer.Put_uint32 (fHueSatMapEncoding);
    499 
    500 				}
    501 
    502 			}
    503 
    504 		if (fLookTable.IsValid ())
    505 			{
    506 
    507 			FingerprintHueSatMap (printer, fLookTable);
    508 
    509 			if (fLookTableEncoding != 0)
    510 				{
    511 
    512 				printer.Put_uint32 (fLookTableEncoding);
    513 
    514 				}
    515 
    516 			}
    517 
    518 		if (fBaselineExposureOffset.IsValid ())
    519 			{
    520 
    521 			if (fBaselineExposureOffset.As_real64 () != 0.0)
    522 				{
    523 
    524 				printer.Put_real64 (fBaselineExposureOffset.As_real64 ());
    525 
    526 				}
    527 
    528 			}
    529 
    530 		if (fDefaultBlackRender != 0)
    531 			{
    532 
    533 			printer.Put_int32 (fDefaultBlackRender);
    534 
    535 			}
    536 
    537 		if (fToneCurve.IsValid ())
    538 			{
    539 
    540 			for (uint32 i = 0; i < fToneCurve.fCoord.size (); i++)
    541 				{
    542 
    543 				printer.Put_real32 ((real32) fToneCurve.fCoord [i].h);
    544 				printer.Put_real32 ((real32) fToneCurve.fCoord [i].v);
    545 
    546 				}
    547 
    548 			}
    549 
    550 		}
    551 
    552 	fFingerprint = printer.Result ();
    553 
    554 	}
    555 
    556 /******************************************************************************/
    557 
    558 bool dng_camera_profile::ValidForwardMatrix (const dng_matrix &m)
    559 	{
    560 
    561 	const real64 kThreshold = 0.01;
    562 
    563 	if (m.NotEmpty ())
    564 		{
    565 
    566 		dng_vector cameraOne;
    567 
    568 		cameraOne.SetIdentity (m.Cols ());
    569 
    570 		dng_vector xyz = m * cameraOne;
    571 
    572 		dng_vector pcs = PCStoXYZ ();
    573 
    574 		if (Abs_real64 (xyz [0] - pcs [0]) > kThreshold ||
    575 			Abs_real64 (xyz [1] - pcs [1]) > kThreshold ||
    576 			Abs_real64 (xyz [2] - pcs [2]) > kThreshold)
    577 			{
    578 
    579 			return false;
    580 
    581 			}
    582 
    583 		}
    584 
    585 	return true;
    586 
    587 	}
    588 
    589 /******************************************************************************/
    590 
    591 bool dng_camera_profile::IsValid (uint32 channels) const
    592 	{
    593 
    594 	// For Monochrome images, we ignore the camera profile.
    595 
    596 	if (channels == 1)
    597 		{
    598 
    599 		return true;
    600 
    601 		}
    602 
    603 	// ColorMatrix1 is required for all color images.
    604 
    605 	if (fColorMatrix1.Cols () != 3 ||
    606 		fColorMatrix1.Rows () != channels)
    607 		{
    608 
    609 		#if qDNGValidate
    610 
    611 		ReportError ("ColorMatrix1 is wrong size");
    612 
    613 		#endif
    614 
    615 		return false;
    616 
    617 		}
    618 
    619 	// ColorMatrix2 is optional, but it must be valid if present.
    620 
    621 	if (fColorMatrix2.Cols () != 0 ||
    622 		fColorMatrix2.Rows () != 0)
    623 		{
    624 
    625 		if (fColorMatrix2.Cols () != 3 ||
    626 			fColorMatrix2.Rows () != channels)
    627 			{
    628 
    629 			#if qDNGValidate
    630 
    631 			ReportError ("ColorMatrix2 is wrong size");
    632 
    633 			#endif
    634 
    635 			return false;
    636 
    637 			}
    638 
    639 		}
    640 
    641 	// ForwardMatrix1 is optional, but it must be valid if present.
    642 
    643 	if (fForwardMatrix1.Cols () != 0 ||
    644 		fForwardMatrix1.Rows () != 0)
    645 		{
    646 
    647 		if (fForwardMatrix1.Rows () != 3 ||
    648 			fForwardMatrix1.Cols () != channels)
    649 			{
    650 
    651 			#if qDNGValidate
    652 
    653 			ReportError ("ForwardMatrix1 is wrong size");
    654 
    655 			#endif
    656 
    657 			return false;
    658 
    659 			}
    660 
    661 		// Make sure ForwardMatrix1 does a valid mapping.
    662 
    663 		if (!ValidForwardMatrix (fForwardMatrix1))
    664 			{
    665 
    666 			#if qDNGValidate
    667 
    668 			ReportError ("ForwardMatrix1 does not map equal camera values to XYZ D50");
    669 
    670 			#endif
    671 
    672 			return false;
    673 
    674 			}
    675 
    676 		}
    677 
    678 	// ForwardMatrix2 is optional, but it must be valid if present.
    679 
    680 	if (fForwardMatrix2.Cols () != 0 ||
    681 		fForwardMatrix2.Rows () != 0)
    682 		{
    683 
    684 		if (fForwardMatrix2.Rows () != 3 ||
    685 			fForwardMatrix2.Cols () != channels)
    686 			{
    687 
    688 			#if qDNGValidate
    689 
    690 			ReportError ("ForwardMatrix2 is wrong size");
    691 
    692 			#endif
    693 
    694 			return false;
    695 
    696 			}
    697 
    698 		// Make sure ForwardMatrix2 does a valid mapping.
    699 
    700 		if (!ValidForwardMatrix (fForwardMatrix2))
    701 			{
    702 
    703 			#if qDNGValidate
    704 
    705 			ReportError ("ForwardMatrix2 does not map equal camera values to XYZ D50");
    706 
    707 			#endif
    708 
    709 			return false;
    710 
    711 			}
    712 
    713 		}
    714 
    715 	// ReductionMatrix1 is optional, but it must be valid if present.
    716 
    717 	if (fReductionMatrix1.Cols () != 0 ||
    718 		fReductionMatrix1.Rows () != 0)
    719 		{
    720 
    721 		if (fReductionMatrix1.Cols () != channels ||
    722 			fReductionMatrix1.Rows () != 3)
    723 			{
    724 
    725 			#if qDNGValidate
    726 
    727 			ReportError ("ReductionMatrix1 is wrong size");
    728 
    729 			#endif
    730 
    731 			return false;
    732 
    733 			}
    734 
    735 		}
    736 
    737 	// ReductionMatrix2 is optional, but it must be valid if present.
    738 
    739 	if (fReductionMatrix2.Cols () != 0 ||
    740 		fReductionMatrix2.Rows () != 0)
    741 		{
    742 
    743 		if (fReductionMatrix2.Cols () != channels ||
    744 			fReductionMatrix2.Rows () != 3)
    745 			{
    746 
    747 			#if qDNGValidate
    748 
    749 			ReportError ("ReductionMatrix2 is wrong size");
    750 
    751 			#endif
    752 
    753 			return false;
    754 
    755 			}
    756 
    757 		}
    758 
    759 	// Make sure ColorMatrix1 is invertable.
    760 
    761 	try
    762 		{
    763 
    764 		if (fReductionMatrix1.NotEmpty ())
    765 			{
    766 
    767 			(void) Invert (fColorMatrix1,
    768 						   fReductionMatrix1);
    769 
    770 			}
    771 
    772 		else
    773 			{
    774 
    775 			(void) Invert (fColorMatrix1);
    776 
    777 			}
    778 
    779 		}
    780 
    781 	catch (...)
    782 		{
    783 
    784 		#if qDNGValidate
    785 
    786 		ReportError ("ColorMatrix1 is not invertable");
    787 
    788 		#endif
    789 
    790 		return false;
    791 
    792 		}
    793 
    794 	// Make sure ColorMatrix2 is invertable.
    795 
    796 	if (fColorMatrix2.NotEmpty ())
    797 		{
    798 
    799 		try
    800 			{
    801 
    802 			if (fReductionMatrix2.NotEmpty ())
    803 				{
    804 
    805 				(void) Invert (fColorMatrix2,
    806 							   fReductionMatrix2);
    807 
    808 				}
    809 
    810 			else
    811 				{
    812 
    813 				(void) Invert (fColorMatrix2);
    814 
    815 				}
    816 
    817 			}
    818 
    819 		catch (...)
    820 			{
    821 
    822 			#if qDNGValidate
    823 
    824 			ReportError ("ColorMatrix2 is not invertable");
    825 
    826 			#endif
    827 
    828 			return false;
    829 
    830 			}
    831 
    832 		}
    833 
    834 	return true;
    835 
    836 	}
    837 
    838 /*****************************************************************************/
    839 
    840 bool dng_camera_profile::EqualData (const dng_camera_profile &profile) const
    841 	{
    842 
    843 	return fCalibrationIlluminant1				== profile.fCalibrationIlluminant1				&&
    844 		   fCalibrationIlluminant2				== profile.fCalibrationIlluminant2				&&
    845 		   fColorMatrix1						== profile.fColorMatrix1						&&
    846 		   fColorMatrix2						== profile.fColorMatrix2						&&
    847 		   fForwardMatrix1						== profile.fForwardMatrix1						&&
    848 		   fForwardMatrix2						== profile.fForwardMatrix2						&&
    849 		   fReductionMatrix1					== profile.fReductionMatrix1					&&
    850 		   fReductionMatrix2					== profile.fReductionMatrix2					&&
    851 		   fHueSatDeltas1						== profile.fHueSatDeltas1						&&
    852 		   fHueSatDeltas2						== profile.fHueSatDeltas2						&&
    853 		   fHueSatMapEncoding					== profile.fHueSatMapEncoding					&&
    854 		   fLookTable							== profile.fLookTable							&&
    855 		   fLookTableEncoding					== profile.fLookTableEncoding					&&
    856 		   fDefaultBlackRender					== profile.fDefaultBlackRender					&&
    857 		   fToneCurve							== profile.fToneCurve							&&
    858 		   fBaselineExposureOffset.As_real64 () == profile.fBaselineExposureOffset.As_real64 () &&
    859 		   fProfileCalibrationSignature			== profile.fProfileCalibrationSignature;
    860 
    861 	}
    862 
    863 /*****************************************************************************/
    864 
    865 void dng_camera_profile::ReadHueSatMap (dng_stream &stream,
    866 										dng_hue_sat_map &hueSatMap,
    867 										uint32 hues,
    868 										uint32 sats,
    869 										uint32 vals,
    870 										bool skipSat0)
    871 	{
    872 
    873 	hueSatMap.SetDivisions (hues, sats, vals);
    874 
    875 	for (uint32 val = 0; val < vals; val++)
    876 		{
    877 
    878 		for (uint32 hue = 0; hue < hues; hue++)
    879 			{
    880 
    881 			for (uint32 sat = skipSat0 ? 1 : 0; sat < sats; sat++)
    882 				{
    883 
    884 				dng_hue_sat_map::HSBModify modify;
    885 
    886 				modify.fHueShift = stream.Get_real32 ();
    887 				modify.fSatScale = stream.Get_real32 ();
    888 				modify.fValScale = stream.Get_real32 ();
    889 
    890 				hueSatMap.SetDelta (hue, sat, val, modify);
    891 
    892 				}
    893 
    894 			}
    895 
    896 		}
    897 
    898 	}
    899 
    900 /*****************************************************************************/
    901 
    902 void dng_camera_profile::Parse (dng_stream &stream,
    903 								dng_camera_profile_info &profileInfo)
    904 	{
    905 
    906 	SetUniqueCameraModelRestriction (profileInfo.fUniqueCameraModel.Get ());
    907 
    908 	if (profileInfo.fProfileName.NotEmpty ())
    909 		{
    910 
    911 		SetName (profileInfo.fProfileName.Get ());
    912 
    913 		}
    914 
    915 	SetCopyright (profileInfo.fProfileCopyright.Get ());
    916 
    917 	SetEmbedPolicy (profileInfo.fEmbedPolicy);
    918 
    919 	SetCalibrationIlluminant1 (profileInfo.fCalibrationIlluminant1);
    920 
    921 	SetColorMatrix1 (profileInfo.fColorMatrix1);
    922 
    923 	if (profileInfo.fForwardMatrix1.NotEmpty ())
    924 		{
    925 
    926 		SetForwardMatrix1 (profileInfo.fForwardMatrix1);
    927 
    928 		}
    929 
    930 	if (profileInfo.fReductionMatrix1.NotEmpty ())
    931 		{
    932 
    933 		SetReductionMatrix1 (profileInfo.fReductionMatrix1);
    934 
    935 		}
    936 
    937 	if (profileInfo.fColorMatrix2.NotEmpty ())
    938 		{
    939 
    940 		SetCalibrationIlluminant2 (profileInfo.fCalibrationIlluminant2);
    941 
    942 		SetColorMatrix2 (profileInfo.fColorMatrix2);
    943 
    944 		if (profileInfo.fForwardMatrix2.NotEmpty ())
    945 			{
    946 
    947 			SetForwardMatrix2 (profileInfo.fForwardMatrix2);
    948 
    949 			}
    950 
    951 		if (profileInfo.fReductionMatrix2.NotEmpty ())
    952 			{
    953 
    954 			SetReductionMatrix2 (profileInfo.fReductionMatrix2);
    955 
    956 			}
    957 
    958 		}
    959 
    960 	SetProfileCalibrationSignature (profileInfo.fProfileCalibrationSignature.Get ());
    961 
    962 	if (profileInfo.fHueSatDeltas1Offset != 0 &&
    963 		profileInfo.fHueSatDeltas1Count  != 0)
    964 		{
    965 
    966 		TempBigEndian setEndianness (stream, profileInfo.fBigEndian);
    967 
    968 		stream.SetReadPosition (profileInfo.fHueSatDeltas1Offset);
    969 
    970 		bool skipSat0 = (profileInfo.fHueSatDeltas1Count == SafeUint32Mult(
    971 														   profileInfo.fProfileHues,
    972 														   SafeUint32Sub(profileInfo.fProfileSats, 1),
    973 														   profileInfo.fProfileVals, 3));
    974 
    975 		ReadHueSatMap (stream,
    976 					   fHueSatDeltas1,
    977 					   profileInfo.fProfileHues,
    978 					   profileInfo.fProfileSats,
    979 					   profileInfo.fProfileVals,
    980 					   skipSat0);
    981 
    982 		}
    983 
    984 	if (profileInfo.fHueSatDeltas2Offset != 0 &&
    985 		profileInfo.fHueSatDeltas2Count  != 0)
    986 		{
    987 
    988 		TempBigEndian setEndianness (stream, profileInfo.fBigEndian);
    989 
    990 		stream.SetReadPosition (profileInfo.fHueSatDeltas2Offset);
    991 
    992 		bool skipSat0 = (profileInfo.fHueSatDeltas2Count == SafeUint32Mult(
    993 														   profileInfo.fProfileHues,
    994 														   SafeUint32Sub(profileInfo.fProfileSats, 1),
    995 														   profileInfo.fProfileVals, 3));
    996 
    997 		ReadHueSatMap (stream,
    998 					   fHueSatDeltas2,
    999 					   profileInfo.fProfileHues,
   1000 					   profileInfo.fProfileSats,
   1001 					   profileInfo.fProfileVals,
   1002 					   skipSat0);
   1003 
   1004 		}
   1005 
   1006 	if (profileInfo.fLookTableOffset != 0 &&
   1007 		profileInfo.fLookTableCount  != 0)
   1008 		{
   1009 
   1010 		TempBigEndian setEndianness (stream, profileInfo.fBigEndian);
   1011 
   1012 		stream.SetReadPosition (profileInfo.fLookTableOffset);
   1013 
   1014 		bool skipSat0 = (profileInfo.fLookTableCount == SafeUint32Mult(
   1015 													   profileInfo.fLookTableHues,
   1016 													   SafeUint32Sub(profileInfo.fLookTableSats, 1),
   1017 														 profileInfo.fLookTableVals, 3));
   1018 
   1019 		ReadHueSatMap (stream,
   1020 					   fLookTable,
   1021 					   profileInfo.fLookTableHues,
   1022 					   profileInfo.fLookTableSats,
   1023 					   profileInfo.fLookTableVals,
   1024 					   skipSat0);
   1025 
   1026 		}
   1027 
   1028 	if ((profileInfo.fToneCurveCount & 1) == 0)
   1029 		{
   1030 
   1031 		TempBigEndian setEndianness (stream, profileInfo.fBigEndian);
   1032 
   1033 		stream.SetReadPosition (profileInfo.fToneCurveOffset);
   1034 
   1035 		uint32 points = profileInfo.fToneCurveCount / 2;
   1036 
   1037 		fToneCurve.fCoord.resize (points);
   1038 
   1039 		for (size_t i = 0; i < points; i++)
   1040 			{
   1041 
   1042 			dng_point_real64 point;
   1043 
   1044 			point.h = stream.Get_real32 ();
   1045 			point.v = stream.Get_real32 ();
   1046 
   1047 			fToneCurve.fCoord [i] = point;
   1048 
   1049 			}
   1050 
   1051 		}
   1052 
   1053 	SetHueSatMapEncoding (profileInfo.fHueSatMapEncoding);
   1054 
   1055 	SetLookTableEncoding (profileInfo.fLookTableEncoding);
   1056 
   1057 	SetBaselineExposureOffset (profileInfo.fBaselineExposureOffset.As_real64 ());
   1058 
   1059 	SetDefaultBlackRender (profileInfo.fDefaultBlackRender);
   1060 
   1061 	}
   1062 
   1063 /*****************************************************************************/
   1064 
   1065 bool dng_camera_profile::ParseExtended (dng_stream &stream)
   1066 	{
   1067 
   1068 	try
   1069 		{
   1070 
   1071 		dng_camera_profile_info profileInfo;
   1072 
   1073 		if (!profileInfo.ParseExtended (stream))
   1074 			{
   1075 			return false;
   1076 			}
   1077 
   1078 		Parse (stream, profileInfo);
   1079 
   1080 		return true;
   1081 
   1082 		}
   1083 
   1084 	catch (...)
   1085 		{
   1086 
   1087 		// Eat parsing errors.
   1088 
   1089 		}
   1090 
   1091 	return false;
   1092 
   1093 	}
   1094 
   1095 /*****************************************************************************/
   1096 
   1097 void dng_camera_profile::SetFourColorBayer ()
   1098 	{
   1099 
   1100 	uint32 j;
   1101 
   1102 	if (!IsValid (3))
   1103 		{
   1104 		ThrowProgramError ();
   1105 		}
   1106 
   1107 	if (fColorMatrix1.NotEmpty ())
   1108 		{
   1109 
   1110 		dng_matrix m (4, 3);
   1111 
   1112 		for (j = 0; j < 3; j++)
   1113 			{
   1114 			m [0] [j] = fColorMatrix1 [0] [j];
   1115 			m [1] [j] = fColorMatrix1 [1] [j];
   1116 			m [2] [j] = fColorMatrix1 [2] [j];
   1117 			m [3] [j] = fColorMatrix1 [1] [j];
   1118 			}
   1119 
   1120 		fColorMatrix1 = m;
   1121 
   1122 		}
   1123 
   1124 	if (fColorMatrix2.NotEmpty ())
   1125 		{
   1126 
   1127 		dng_matrix m (4, 3);
   1128 
   1129 		for (j = 0; j < 3; j++)
   1130 			{
   1131 			m [0] [j] = fColorMatrix2 [0] [j];
   1132 			m [1] [j] = fColorMatrix2 [1] [j];
   1133 			m [2] [j] = fColorMatrix2 [2] [j];
   1134 			m [3] [j] = fColorMatrix2 [1] [j];
   1135 			}
   1136 
   1137 		fColorMatrix2 = m;
   1138 
   1139 		}
   1140 
   1141 	fReductionMatrix1.Clear ();
   1142 	fReductionMatrix2.Clear ();
   1143 
   1144 	fForwardMatrix1.Clear ();
   1145 	fForwardMatrix2.Clear ();
   1146 
   1147 	}
   1148 
   1149 /*****************************************************************************/
   1150 
   1151 dng_hue_sat_map * dng_camera_profile::HueSatMapForWhite (const dng_xy_coord &white) const
   1152 	{
   1153 
   1154 	if (fHueSatDeltas1.IsValid ())
   1155 		{
   1156 
   1157 		// If we only have the first table, just use it for any color temperature.
   1158 
   1159 		if (!fHueSatDeltas2.IsValid ())
   1160 			{
   1161 
   1162 			return new dng_hue_sat_map (fHueSatDeltas1);
   1163 
   1164 			}
   1165 
   1166 		// Else we need to interpolate based on color temperature.
   1167 
   1168 		real64 temperature1 = CalibrationTemperature1 ();
   1169 		real64 temperature2 = CalibrationTemperature2 ();
   1170 
   1171 		if (temperature1 <= 0.0 ||
   1172 			temperature2 <= 0.0 ||
   1173 			temperature1 == temperature2)
   1174 			{
   1175 
   1176 			return new dng_hue_sat_map (fHueSatDeltas1);
   1177 
   1178 			}
   1179 
   1180 		bool reverseOrder = temperature1 > temperature2;
   1181 
   1182 		if (reverseOrder)
   1183 			{
   1184 			real64 temp  = temperature1;
   1185 			temperature1 = temperature2;
   1186 			temperature2 = temp;
   1187 			}
   1188 
   1189 		// Convert to temperature/offset space.
   1190 
   1191 		dng_temperature td (white);
   1192 
   1193 		// Find fraction to weight the first calibration.
   1194 
   1195 		real64 g;
   1196 
   1197 		if (td.Temperature () <= temperature1)
   1198 			g = 1.0;
   1199 
   1200 		else if (td.Temperature () >= temperature2)
   1201 			g = 0.0;
   1202 
   1203 		else
   1204 			{
   1205 
   1206 			real64 invT = 1.0 / td.Temperature ();
   1207 
   1208 			g = (invT                 - (1.0 / temperature2)) /
   1209 				((1.0 / temperature1) - (1.0 / temperature2));
   1210 
   1211 			}
   1212 
   1213 		// Fix up if we swapped the order.
   1214 
   1215 		if (reverseOrder)
   1216 			{
   1217 			g = 1.0 - g;
   1218 			}
   1219 
   1220 		// Do the interpolation.
   1221 
   1222 		return dng_hue_sat_map::Interpolate (HueSatDeltas1 (),
   1223 											 HueSatDeltas2 (),
   1224 											 g);
   1225 
   1226 		}
   1227 
   1228 	return NULL;
   1229 
   1230 	}
   1231 
   1232 /*****************************************************************************/
   1233 
   1234 void dng_camera_profile::Stub ()
   1235 	{
   1236 
   1237 	(void) Fingerprint ();
   1238 
   1239 	dng_hue_sat_map nullTable;
   1240 
   1241 	fHueSatDeltas1 = nullTable;
   1242 	fHueSatDeltas2 = nullTable;
   1243 
   1244 	fLookTable = nullTable;
   1245 
   1246 	fToneCurve.SetInvalid ();
   1247 
   1248 	fWasStubbed = true;
   1249 
   1250 	}
   1251 
   1252 /*****************************************************************************/
   1253 
   1254 void SplitCameraProfileName (const dng_string &name,
   1255 							 dng_string &baseName,
   1256 							 int32 &version)
   1257 	{
   1258 
   1259 	baseName = name;
   1260 
   1261 	version = 0;
   1262 
   1263 	uint32 len = baseName.Length ();
   1264 
   1265 	if (len > 5 && baseName.EndsWith (" beta"))
   1266 		{
   1267 
   1268 		baseName.Truncate (len - 5);
   1269 
   1270 		version += -10;
   1271 
   1272 		}
   1273 
   1274 	else if (len > 7)
   1275 		{
   1276 
   1277 		char lastChar = name.Get () [len - 1];
   1278 
   1279 		if (lastChar >= '0' && lastChar <= '9')
   1280 			{
   1281 
   1282 			dng_string temp = name;
   1283 
   1284 			temp.Truncate (len - 1);
   1285 
   1286 			if (temp.EndsWith (" beta "))
   1287 				{
   1288 
   1289 				baseName.Truncate (len - 7);
   1290 
   1291 				version += ((int32) (lastChar - '0')) - 10;
   1292 
   1293 				}
   1294 
   1295 			}
   1296 
   1297 		}
   1298 
   1299 	len = baseName.Length ();
   1300 
   1301 	if (len > 3)
   1302 		{
   1303 
   1304 		char lastChar = name.Get () [len - 1];
   1305 
   1306 		if (lastChar >= '0' && lastChar <= '9')
   1307 			{
   1308 
   1309 			dng_string temp = name;
   1310 
   1311 			temp.Truncate (len - 1);
   1312 
   1313 			if (temp.EndsWith (" v"))
   1314 				{
   1315 
   1316 				baseName.Truncate (len - 3);
   1317 
   1318 				version += ((int32) (lastChar - '0')) * 100;
   1319 
   1320 				}
   1321 
   1322 			}
   1323 
   1324 		}
   1325 
   1326 	}
   1327 
   1328 /*****************************************************************************/
   1329 
   1330 void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator,
   1331 								  uint32 encoding,
   1332 								  AutoPtr<dng_1d_table> &encodeTable,
   1333 								  AutoPtr<dng_1d_table> &decodeTable,
   1334 								  bool subSample)
   1335 	{
   1336 
   1337 	encodeTable.Reset ();
   1338 	decodeTable.Reset ();
   1339 
   1340 	switch (encoding)
   1341 		{
   1342 
   1343 		case encoding_Linear:
   1344 			{
   1345 
   1346 			break;
   1347 
   1348 			}
   1349 
   1350 		case encoding_sRGB:
   1351 			{
   1352 
   1353 			encodeTable.Reset (new dng_1d_table);
   1354 			decodeTable.Reset (new dng_1d_table);
   1355 
   1356 			const dng_1d_function & curve = dng_function_GammaEncode_sRGB::Get ();
   1357 
   1358 			encodeTable->Initialize (allocator,
   1359 									 curve,
   1360 									 subSample);
   1361 
   1362 			const dng_1d_inverse inverse (curve);
   1363 
   1364 			decodeTable->Initialize (allocator,
   1365 									 inverse,
   1366 									 subSample);
   1367 
   1368 			break;
   1369 
   1370 			}
   1371 
   1372 		default:
   1373 			{
   1374 
   1375 			DNG_REPORT ("Unsupported hue sat map / look table encoding.");
   1376 
   1377 			break;
   1378 
   1379 			}
   1380 
   1381 		}
   1382 
   1383 	}
   1384 
   1385 /*****************************************************************************/
   1386