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