18e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
28e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener// Copyright 2006-2008 Adobe Systems Incorporated
38e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener// All Rights Reserved.
48e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener//
58e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener// NOTICE:  Adobe permits you to use, modify, and distribute this file in
68e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener// accordance with the terms of the Adobe license agreement accompanying it.
78e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
88e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
98e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_spec.cpp#1 $ */
108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/* $DateTime: 2012/05/30 13:28:51 $ */
118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/* $Change: 832332 $ */
128e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/* $Author: tknoll $ */
138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_color_spec.h"
158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_assertions.h"
178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_camera_profile.h"
188e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_exceptions.h"
198e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_matrix.h"
208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_negative.h"
218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_temperature.h"
228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_utils.h"
238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener#include "dng_xy_coord.h"
248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
278e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerdng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1,
288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener						        const dng_xy_coord &white2)
298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Use the linearized Bradford adaptation matrix.
328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix_3by3 Mb ( 0.8951,  0.2664, -0.1614,
348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		 		        -0.7502,  1.7135,  0.0367,
358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		  			     0.0389, -0.0685,  1.0296);
368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
378e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_vector_3 w1 = Mb * XYtoXYZ (white1);
388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_vector_3 w2 = Mb * XYtoXYZ (white2);
398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Negative white coordinates are kind of meaningless.
418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w1 [0] = Max_real64 (w1 [0], 0.0);
438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w1 [1] = Max_real64 (w1 [1], 0.0);
448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w1 [2] = Max_real64 (w1 [2], 0.0);
458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w2 [0] = Max_real64 (w2 [0], 0.0);
478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w2 [1] = Max_real64 (w2 [1], 0.0);
488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	w2 [2] = Max_real64 (w2 [2], 0.0);
498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Limit scaling to something reasonable.
518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix_3by3 A;
538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	A [0] [0] = Pin_real64 (0.1, w1 [0] > 0.0 ? w2 [0] / w1 [0] : 10.0, 10.0);
558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	A [1] [1] = Pin_real64 (0.1, w1 [1] > 0.0 ? w2 [1] / w1 [1] : 10.0, 10.0);
568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	A [2] [2] = Pin_real64 (0.1, w1 [2] > 0.0 ? w2 [2] / w1 [2] : 10.0, 10.0);
578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix_3by3 B = Invert (Mb) * A * Mb;
598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return B;
618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/******************************************************************************/
658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
668e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerdng_color_spec::dng_color_spec (const dng_negative &negative,
678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener							    const dng_camera_profile *profile)
688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
698e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	:	fChannels (negative.ColorChannels ())
708e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
718e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fTemperature1 (0.0)
728e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fTemperature2 (0.0)
738e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
748e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fColorMatrix1 ()
758e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fColorMatrix2 ()
768e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
778e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fForwardMatrix1 ()
788e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fForwardMatrix2 ()
798e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
808e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fReductionMatrix1 ()
818e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fReductionMatrix2 ()
828e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
838e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fCameraCalibration1 ()
848e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fCameraCalibration2 ()
858e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
868e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fAnalogBalance ()
878e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
888e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fWhiteXY ()
898e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
908e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fCameraWhite ()
918e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fCameraToPCS ()
928e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
938e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	,	fPCStoCamera ()
948e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
958e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
968e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
978e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (fChannels > 1)
988e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
998e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1008e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (!profile || !profile->IsValid (fChannels))
1018e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1028e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			ThrowBadFormat ();
1038e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
1048e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1058e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (profile->WasStubbed ())
1068e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1078e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			ThrowProgramError ("Using stubbed profile");
1088e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
1098e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fTemperature1 = profile->CalibrationTemperature1 ();
1118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fTemperature2 = profile->CalibrationTemperature2 ();
1128e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fColorMatrix1 = profile->ColorMatrix1 ();
1148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fColorMatrix2 = profile->ColorMatrix2 ();
1158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fForwardMatrix1 = profile->ForwardMatrix1 ();
1178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fForwardMatrix2 = profile->ForwardMatrix2 ();
1188e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1198e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fReductionMatrix1 = profile->ReductionMatrix1 ();
1208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fReductionMatrix2 = profile->ReductionMatrix2 ();
1218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraCalibration1.SetIdentity (fChannels);
1238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraCalibration2.SetIdentity (fChannels);
1248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (negative. CameraCalibrationSignature () ==
1268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			profile->ProfileCalibrationSignature ())
1278e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			if (negative.CameraCalibration1 ().Rows () == fChannels &&
1308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				negative.CameraCalibration1 ().Cols () == fChannels)
1318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				{
1328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fCameraCalibration1 = negative.CameraCalibration1 ();
1348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				}
1368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1378e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			if (negative.CameraCalibration2 ().Rows () == fChannels &&
1388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				negative.CameraCalibration2 ().Cols () == fChannels)
1398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				{
1408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fCameraCalibration2 = negative.CameraCalibration2 ();
1428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				}
1448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
1468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fAnalogBalance = dng_matrix (fChannels, fChannels);
1488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		for (uint32 j = 0; j < fChannels; j++)
1508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fAnalogBalance [j] [j] = negative.AnalogBalance (j);
1538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
1558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix1);
1578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1;
1598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (!profile->HasColorMatrix2 () ||
1618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fTemperature1 <= 0.0 ||
1628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fTemperature2 <= 0.0 ||
1638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fTemperature1 == fTemperature2)
1648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1668e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fTemperature1 = 5000.0;
1678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fTemperature2 = 5000.0;
1688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1698e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fColorMatrix2       = fColorMatrix1;
1708e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fForwardMatrix2     = fForwardMatrix1;
1718e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fReductionMatrix2   = fReductionMatrix1;
1728e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fCameraCalibration2 = fCameraCalibration1;
1738e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1748e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
1758e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1768e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else
1778e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
1788e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1798e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix2);
1808e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1818e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2;
1828e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1838e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			// Swap values if temperatures are out of order.
1848e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1858e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			if (fTemperature1 > fTemperature2)
1868e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				{
1878e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1888e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				real64 temp   = fTemperature1;
1898e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fTemperature1 = fTemperature2;
1908e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fTemperature2 = temp;
1918e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1928e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				dng_matrix T  = fColorMatrix1;
1938e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fColorMatrix1 = fColorMatrix2;
1948e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fColorMatrix2 = T;
1958e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
1968e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				T               = fForwardMatrix1;
1978e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fForwardMatrix1 = fForwardMatrix2;
1988e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fForwardMatrix2 = T;
1998e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2008e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				T                 = fReductionMatrix1;
2018e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fReductionMatrix1 = fReductionMatrix2;
2028e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fReductionMatrix2 = T;
2038e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2048e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				T                   = fCameraCalibration1;
2058e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fCameraCalibration1 = fCameraCalibration2;
2068e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				fCameraCalibration2 = T;
2078e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2088e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				}
2098e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
2118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2128e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
2138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
2158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
2178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2188e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerdng_matrix dng_color_spec::FindXYZtoCamera (const dng_xy_coord &white,
2198e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener											dng_matrix *forwardMatrix,
2208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener									        dng_matrix *reductionMatrix,
2218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener											dng_matrix *cameraCalibration)
2228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
2238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Convert to temperature/offset space.
2258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_temperature td (white);
2278e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Find fraction to weight the first calibration.
2298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	real64 g;
2318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (td.Temperature () <= fTemperature1)
2338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		g = 1.0;
2348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	else if (td.Temperature () >= fTemperature2)
2368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		g = 0.0;
2378e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	else
2398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
2408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		real64 invT = 1.0 / td.Temperature ();
2428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		g = (invT                  - (1.0 / fTemperature2)) /
2448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		    ((1.0 / fTemperature1) - (1.0 / fTemperature2));
2458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
2478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Interpolate the color matrix.
2498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix colorMatrix;
2518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (g >= 1.0)
2538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		colorMatrix = fColorMatrix1;
2548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	else if (g <= 0.0)
2568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		colorMatrix = fColorMatrix2;
2578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	else
2598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		colorMatrix = (g      ) * fColorMatrix1 +
2608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener					  (1.0 - g) * fColorMatrix2;
2618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Interpolate forward matrix, if any.
2638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (forwardMatrix)
2658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
2668e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		bool has1 = fForwardMatrix1.NotEmpty ();
2688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		bool has2 = fForwardMatrix2.NotEmpty ();
2698e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2708e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (has1 && has2)
2718e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
2728e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2738e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			if (g >= 1.0)
2748e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*forwardMatrix = fForwardMatrix1;
2758e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2768e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			else if (g <= 0.0)
2778e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*forwardMatrix = fForwardMatrix2;
2788e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2798e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			else
2808e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*forwardMatrix = (g      ) * fForwardMatrix1 +
2818e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener								 (1.0 - g) * fForwardMatrix2;
2828e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2838e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
2848e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2858e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else if (has1)
2868e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
2878e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2888e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*forwardMatrix = fForwardMatrix1;
2898e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2908e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
2918e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2928e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else if (has2)
2938e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
2948e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2958e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*forwardMatrix = fForwardMatrix2;
2968e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2978e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
2988e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
2998e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else
3008e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
3018e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3028e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			forwardMatrix->Clear ();
3038e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3048e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
3058e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3068e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
3078e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3088e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Interpolate reduction matrix, if any.
3098e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (reductionMatrix)
3118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
3128e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		bool has1 = fReductionMatrix1.NotEmpty ();
3148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		bool has2 = fReductionMatrix2.NotEmpty ();
3158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (has1 && has2)
3178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
3188e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3198e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			if (g >= 1.0)
3208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*reductionMatrix = fReductionMatrix1;
3218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			else if (g <= 0.0)
3238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*reductionMatrix = fReductionMatrix2;
3248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			else
3268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener				*reductionMatrix = (g      ) * fReductionMatrix1 +
3278e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener								   (1.0 - g) * fReductionMatrix2;
3288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
3308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else if (has1)
3328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
3338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*reductionMatrix = fReductionMatrix1;
3358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
3378e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else if (has2)
3398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
3408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*reductionMatrix = fReductionMatrix2;
3428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
3448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else
3468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
3478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			reductionMatrix->Clear ();
3498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
3518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
3538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Interpolate camera calibration matrix.
3558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (cameraCalibration)
3578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
3588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (g >= 1.0)
3608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*cameraCalibration = fCameraCalibration1;
3618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else if (g <= 0.0)
3638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*cameraCalibration = fCameraCalibration2;
3648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		else
3668e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			*cameraCalibration = (g      ) * fCameraCalibration1 +
3678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener							     (1.0 - g) * fCameraCalibration2;
3688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3698e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
3708e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3718e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Return the interpolated color matrix.
3728e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3738e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return colorMatrix;
3748e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3758e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
3768e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3778e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
3788e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3798e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienervoid dng_color_spec::SetWhiteXY (const dng_xy_coord &white)
3808e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
3818e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3828e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	fWhiteXY = white;
3838e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3848e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Deal with monochrome cameras.
3858e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3868e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (fChannels == 1)
3878e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
3888e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3898e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraWhite.SetIdentity (1);
3908e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3918e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraToPCS = PCStoXYZ ().AsColumn ();
3928e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3938e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		return;
3948e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3958e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
3968e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3978e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Interpolate an matric values for this white point.
3988e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
3998e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix colorMatrix;
4008e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix forwardMatrix;
4018e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix reductionMatrix;
4028e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_matrix cameraCalibration;
4038e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4048e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	colorMatrix = FindXYZtoCamera (fWhiteXY,
4058e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener								   &forwardMatrix,
4068e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener								   &reductionMatrix,
4078e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener								   &cameraCalibration);
4088e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4098e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Find the camera white values.
4108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	fCameraWhite = colorMatrix * XYtoXYZ (fWhiteXY);
4127841298310de58d0cddb212ac6295d9a27cf547cFlorian Kriener
4138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	real64 cameraWhiteMaxEntry = MaxEntry (fCameraWhite);
4148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (cameraWhiteMaxEntry == 0)
4158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
4168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		 ThrowBadFormat ();
4178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
4188e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	real64 whiteScale = 1.0 / cameraWhiteMaxEntry;
4198e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	for (uint32 j = 0; j < fChannels; j++)
4218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
4228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// We don't support non-positive values for camera neutral values.
4248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraWhite [j] = Pin_real64 (0.001,
4268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener									   whiteScale * fCameraWhite [j],
4278e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener									   1.0);
4288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
4308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Find PCS to Camera transform. Scale matrix so PCS white can just be
4328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// reached when the first camera channel saturates
4338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	fPCStoCamera = colorMatrix * MapWhiteMatrix (PCStoXY (), fWhiteXY);
4358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	real64 scale = MaxEntry (fPCStoCamera * PCStoXYZ ());
4377841298310de58d0cddb212ac6295d9a27cf547cFlorian Kriener
4388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (scale == 0)
4398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
4408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		 ThrowBadFormat ();
4418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
4428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	fPCStoCamera = (1.0 / scale) * fPCStoCamera;
4438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// If we have a forward matrix, then just use that.
4458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (forwardMatrix.NotEmpty ())
4478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
4488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		dng_matrix individualToReference = Invert (fAnalogBalance * cameraCalibration);
4508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		dng_vector refCameraWhite = individualToReference * fCameraWhite;
4528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraToPCS = forwardMatrix *
4548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener					   Invert (refCameraWhite.AsDiagonal ()) *
4558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener					   individualToReference;
4568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
4588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	// Else we need to use the adapt in XYZ method.
4608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	else
4628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
4638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// Invert this PCS to camera matrix.  Note that if there are more than three
4658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// camera channels, this inversion is non-unique.
4668e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		fCameraToPCS = Invert (fPCStoCamera, reductionMatrix);
4688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4698e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
4708e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4718e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
4728e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4738e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
4748e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4758e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerconst dng_xy_coord & dng_color_spec::WhiteXY () const
4768e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
4778e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4788e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	DNG_ASSERT (fWhiteXY.IsValid (), "Using invalid WhiteXY");
4798e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4808e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return fWhiteXY;
4818e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4828e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
4838e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4848e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
4858e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4868e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerconst dng_vector & dng_color_spec::CameraWhite () const
4878e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
4888e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4898e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	DNG_ASSERT (fCameraWhite.NotEmpty (), "Using invalid CameraWhite");
4908e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4918e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return fCameraWhite;
4928e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4938e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
4948e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4958e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
4968e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
4978e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerconst dng_matrix & dng_color_spec::CameraToPCS () const
4988e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
4998e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5008e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	DNG_ASSERT (fCameraToPCS.NotEmpty (), "Using invalid CameraToPCS");
5018e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5028e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return fCameraToPCS;
5038e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5048e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
5058e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5068e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
5078e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5088e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerconst dng_matrix & dng_color_spec::PCStoCamera () const
5098e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
5108e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5118e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	DNG_ASSERT (fPCStoCamera.NotEmpty (), "Using invalid PCStoCamera");
5128e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5138e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return fPCStoCamera;
5148e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5158e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
5168e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5178e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
5188e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5198e8939fc10ad63576a2785ba1333a23726b7e164Florian Krienerdng_xy_coord dng_color_spec::NeutralToXY (const dng_vector &neutral)
5208e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	{
5218e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5228e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	const uint32 kMaxPasses = 30;
5238e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5248e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	if (fChannels == 1)
5258e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
5268e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5278e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		return PCStoXY ();
5288e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5298e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
5308e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5318e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	dng_xy_coord last = D50_xy_coord ();
5328e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5338e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	for (uint32 pass = 0; pass < kMaxPasses; pass++)
5348e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		{
5358e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5368e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		dng_matrix xyzToCamera = FindXYZtoCamera (last);
5378e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5388e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		dng_xy_coord next = XYZtoXY (Invert (xyzToCamera) * neutral);
5398e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5408e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (Abs_real64 (next.x - last.x) +
5418e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			Abs_real64 (next.y - last.y) < 0.0000001)
5428e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
5438e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5448e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			return next;
5458e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5468e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
5478e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5488e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// If we reach the limit without converging, we are most likely
5498e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// in a two value oscillation.  So take the average of the last
5508e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		// two estimates and give up.
5518e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5528e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		if (pass == kMaxPasses - 1)
5538e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			{
5548e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5558e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			next.x = (last.x + next.x) * 0.5;
5568e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			next.y = (last.y + next.y) * 0.5;
5578e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5588e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener			}
5598e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5608e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		last = next;
5618e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5628e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener		}
5638e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5648e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	return last;
5658e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5668e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener	}
5678e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener
5688e8939fc10ad63576a2785ba1333a23726b7e164Florian Kriener/*****************************************************************************/
569