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
32const char * kProfileName_Embedded = "Embedded";
33
34const char * kAdobeCalibrationSignature = "com.adobe";
35
36/*****************************************************************************/
37
38dng_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
75dng_camera_profile::~dng_camera_profile ()
76	{
77
78	}
79
80/*****************************************************************************/
81
82real64 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
163void 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
192void 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
205void 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
220void 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
242void 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
255void 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
268void 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
281void 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
294bool dng_camera_profile::HasColorMatrix1 () const
295	{
296
297	return fColorMatrix1.Cols () == 3 &&
298		   fColorMatrix1.Rows ()  > 1;
299
300	}
301
302/*****************************************************************************/
303
304bool dng_camera_profile::HasColorMatrix2 () const
305	{
306
307	return fColorMatrix2.Cols () == 3 &&
308		   fColorMatrix2.Rows () == fColorMatrix1.Rows ();
309
310	}
311
312/*****************************************************************************/
313
314void dng_camera_profile::SetHueSatDeltas1 (const dng_hue_sat_map &deltas1)
315	{
316
317	fHueSatDeltas1 = deltas1;
318
319	ClearFingerprint ();
320
321	}
322
323/*****************************************************************************/
324
325void dng_camera_profile::SetHueSatDeltas2 (const dng_hue_sat_map &deltas2)
326	{
327
328	fHueSatDeltas2 = deltas2;
329
330	ClearFingerprint ();
331
332	}
333
334/*****************************************************************************/
335
336void dng_camera_profile::SetLookTable (const dng_hue_sat_map &table)
337	{
338
339	fLookTable = table;
340
341	ClearFingerprint ();
342
343	}
344
345/*****************************************************************************/
346
347static 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
361static 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
397void 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
558bool 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
591bool 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
840bool 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
865void 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
902void 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
1065bool 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
1097void 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
1151dng_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
1234void 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
1254void 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
1330void 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