/*****************************************************************************/
// Copyright 2006-2008 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_matrix.cpp#1 $ */
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_matrix.h"
#include "dng_exceptions.h"
#include "dng_utils.h"
/*****************************************************************************/
dng_matrix::dng_matrix ()
: fRows (0)
, fCols (0)
{
}
/*****************************************************************************/
dng_matrix::dng_matrix (uint32 rows,
uint32 cols)
: fRows (0)
, fCols (0)
{
if (rows < 1 || rows > kMaxColorPlanes ||
cols < 1 || cols > kMaxColorPlanes)
{
ThrowProgramError ();
}
fRows = rows;
fCols = cols;
for (uint32 row = 0; row < fRows; row++)
for (uint32 col = 0; col < fCols; col++)
{
fData [row] [col] = 0.0;
}
}
/*****************************************************************************/
dng_matrix::dng_matrix (const dng_matrix &m)
: fRows (m.fRows)
, fCols (m.fCols)
{
for (uint32 row = 0; row < fRows; row++)
for (uint32 col = 0; col < fCols; col++)
{
fData [row] [col] = m.fData [row] [col];
}
}
/*****************************************************************************/
void dng_matrix::Clear ()
{
fRows = 0;
fCols = 0;
}
/*****************************************************************************/
void dng_matrix::SetIdentity (uint32 count)
{
*this = dng_matrix (count, count);
for (uint32 j = 0; j < count; j++)
{
fData [j] [j] = 1.0;
}
}
/******************************************************************************/
bool dng_matrix::operator== (const dng_matrix &m) const
{
if (Rows () != m.Rows () ||
Cols () != m.Cols ())
{
return false;
}
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
if (fData [j] [k] != m.fData [j] [k])
{
return false;
}
}
return true;
}
/******************************************************************************/
bool dng_matrix::IsDiagonal () const
{
if (IsEmpty ())
{
return false;
}
if (Rows () != Cols ())
{
return false;
}
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
if (j != k)
{
if (fData [j] [k] != 0.0)
{
return false;
}
}
}
return true;
}
/******************************************************************************/
real64 dng_matrix::MaxEntry () const
{
if (IsEmpty ())
{
return 0.0;
}
real64 m = fData [0] [0];
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
m = Max_real64 (m, fData [j] [k]);
}
return m;
}
/******************************************************************************/
real64 dng_matrix::MinEntry () const
{
if (IsEmpty ())
{
return 0.0;
}
real64 m = fData [0] [0];
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
m = Min_real64 (m, fData [j] [k]);
}
return m;
}
/*****************************************************************************/
void dng_matrix::Scale (real64 factor)
{
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
fData [j] [k] *= factor;
}
}
/*****************************************************************************/
void dng_matrix::Round (real64 factor)
{
real64 invFactor = 1.0 / factor;
for (uint32 j = 0; j < Rows (); j++)
for (uint32 k = 0; k < Cols (); k++)
{
fData [j] [k] = Round_int32 (fData [j] [k] * factor) * invFactor;
}
}
/*****************************************************************************/
void dng_matrix::SafeRound (real64 factor)
{
real64 invFactor = 1.0 / factor;
for (uint32 j = 0; j < Rows (); j++)
{
// Round each row to the specified accuracy, but make sure the
// a rounding does not affect the total of the elements in a row
// more than necessary.
real64 error = 0.0;
for (uint32 k = 0; k < Cols (); k++)
{
fData [j] [k] += error;
real64 rounded = Round_int32 (fData [j] [k] * factor) * invFactor;
error = fData [j] [k] - rounded;
fData [j] [k] = rounded;
}
}
}
/*****************************************************************************/
dng_matrix_3by3::dng_matrix_3by3 ()
: dng_matrix (3, 3)
{
}
/*****************************************************************************/
dng_matrix_3by3::dng_matrix_3by3 (const dng_matrix &m)
: dng_matrix (m)
{
if (Rows () != 3 ||
Cols () != 3)
{
ThrowMatrixMath ();
}
}
/*****************************************************************************/
dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a01, real64 a02,
real64 a10, real64 a11, real64 a12,
real64 a20, real64 a21, real64 a22)
: dng_matrix (3, 3)
{
fData [0] [0] = a00;
fData [0] [1] = a01;
fData [0] [2] = a02;
fData [1] [0] = a10;
fData [1] [1] = a11;
fData [1] [2] = a12;
fData [2] [0] = a20;
fData [2] [1] = a21;
fData [2] [2] = a22;
}
/*****************************************************************************/
dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a11, real64 a22)
: dng_matrix (3, 3)
{
fData [0] [0] = a00;
fData [1] [1] = a11;
fData [2] [2] = a22;
}
/*****************************************************************************/
dng_matrix_4by3::dng_matrix_4by3 ()
: dng_matrix (4, 3)
{
}
/*****************************************************************************/
dng_matrix_4by3::dng_matrix_4by3 (const dng_matrix &m)
: dng_matrix (m)
{
if (Rows () != 4 ||
Cols () != 3)
{
ThrowMatrixMath ();
}
}
/*****************************************************************************/
dng_matrix_4by3::dng_matrix_4by3 (real64 a00, real64 a01, real64 a02,
real64 a10, real64 a11, real64 a12,
real64 a20, real64 a21, real64 a22,
real64 a30, real64 a31, real64 a32)
: dng_matrix (4, 3)
{
fData [0] [0] = a00;
fData [0] [1] = a01;
fData [0] [2] = a02;
fData [1] [0] = a10;
fData [1] [1] = a11;
fData [1] [2] = a12;
fData [2] [0] = a20;
fData [2] [1] = a21;
fData [2] [2] = a22;
fData [3] [0] = a30;
fData [3] [1] = a31;
fData [3] [2] = a32;
}
/*****************************************************************************/
dng_vector::dng_vector ()
: fCount (0)
{
}
/*****************************************************************************/
dng_vector::dng_vector (uint32 count)
: fCount (0)
{
if (count < 1 || count > kMaxColorPlanes)
{
ThrowProgramError ();
}
fCount = count;
for (uint32 index = 0; index < fCount; index++)
{
fData [index] = 0.0;
}
}
/*****************************************************************************/
dng_vector::dng_vector (const dng_vector &v)
: fCount (v.fCount)
{
for (uint32 index = 0; index < fCount; index++)
{
fData [index] = v.fData [index];
}
}
/*****************************************************************************/
void dng_vector::Clear ()
{
fCount = 0;
}
/*****************************************************************************/
void dng_vector::SetIdentity (uint32 count)
{
*this = dng_vector (count);
for (uint32 j = 0; j < count; j++)
{
fData [j] = 1.0;
}
}
/******************************************************************************/
bool dng_vector::operator== (const dng_vector &v) const
{
if (Count () != v.Count ())
{
return false;
}
for (uint32 j = 0; j < Count (); j++)
{
if (fData [j] != v.fData [j])
{
return false;
}
}
return true;
}
/******************************************************************************/
real64 dng_vector::MaxEntry () const
{
if (IsEmpty ())
{
return 0.0;
}
real64 m = fData [0];
for (uint32 j = 0; j < Count (); j++)
{
m = Max_real64 (m, fData [j]);
}
return m;
}
/******************************************************************************/
real64 dng_vector::MinEntry () const
{
if (IsEmpty ())
{
return 0.0;
}
real64 m = fData [0];
for (uint32 j = 0; j < Count (); j++)
{
m = Min_real64 (m, fData [j]);
}
return m;
}
/*****************************************************************************/
void dng_vector::Scale (real64 factor)
{
for (uint32 j = 0; j < Count (); j++)
{
fData [j] *= factor;
}
}
/*****************************************************************************/
void dng_vector::Round (real64 factor)
{
real64 invFactor = 1.0 / factor;
for (uint32 j = 0; j < Count (); j++)
{
fData [j] = Round_int32 (fData [j] * factor) * invFactor;
}
}
/*****************************************************************************/
dng_matrix dng_vector::AsDiagonal () const
{
dng_matrix M (Count (), Count ());
for (uint32 j = 0; j < Count (); j++)
{
M [j] [j] = fData [j];
}
return M;
}
/*****************************************************************************/
dng_matrix dng_vector::AsColumn () const
{
dng_matrix M (Count (), 1);
for (uint32 j = 0; j < Count (); j++)
{
M [j] [0] = fData [j];
}
return M;
}
/******************************************************************************/
dng_vector_3::dng_vector_3 ()
: dng_vector (3)
{
}
/******************************************************************************/
dng_vector_3::dng_vector_3 (const dng_vector &v)
: dng_vector (v)
{
if (Count () != 3)
{
ThrowMatrixMath ();
}
}
/******************************************************************************/
dng_vector_3::dng_vector_3 (real64 a0,
real64 a1,
real64 a2)
: dng_vector (3)
{
fData [0] = a0;
fData [1] = a1;
fData [2] = a2;
}
/******************************************************************************/
dng_vector_4::dng_vector_4 ()
: dng_vector (4)
{
}
/******************************************************************************/
dng_vector_4::dng_vector_4 (const dng_vector &v)
: dng_vector (v)
{
if (Count () != 4)
{
ThrowMatrixMath ();
}
}
/******************************************************************************/
dng_vector_4::dng_vector_4 (real64 a0,
real64 a1,
real64 a2,
real64 a3)
: dng_vector (4)
{
fData [0] = a0;
fData [1] = a1;
fData [2] = a2;
fData [3] = a3;
}
/******************************************************************************/
dng_matrix operator* (const dng_matrix &A,
const dng_matrix &B)
{
if (A.Cols () != B.Rows ())
{
ThrowMatrixMath ();
}
dng_matrix C (A.Rows (), B.Cols ());
for (uint32 j = 0; j < C.Rows (); j++)
for (uint32 k = 0; k < C.Cols (); k++)
{
C [j] [k] = 0.0;
for (uint32 m = 0; m < A.Cols (); m++)
{
real64 aa = A [j] [m];
real64 bb = B [m] [k];
C [j] [k] += aa * bb;
}
}
return C;
}
/******************************************************************************/
dng_vector operator* (const dng_matrix &A,
const dng_vector &B)
{
if (A.Cols () != B.Count ())
{
ThrowMatrixMath ();
}
dng_vector C (A.Rows ());
for (uint32 j = 0; j < C.Count (); j++)
{
C [j] = 0.0;
for (uint32 m = 0; m < A.Cols (); m++)
{
real64 aa = A [j] [m];
real64 bb = B [m];
C [j] += aa * bb;
}
}
return C;
}
/******************************************************************************/
dng_matrix operator* (real64 scale,
const dng_matrix &A)
{
dng_matrix B (A);
B.Scale (scale);
return B;
}
/******************************************************************************/
dng_vector operator* (real64 scale,
const dng_vector &A)
{
dng_vector B (A);
B.Scale (scale);
return B;
}
/******************************************************************************/
dng_matrix operator+ (const dng_matrix &A,
const dng_matrix &B)
{
if (A.Cols () != B.Cols () ||
A.Rows () != B.Rows ())
{
ThrowMatrixMath ();
}
dng_matrix C (A);
for (uint32 j = 0; j < C.Rows (); j++)
for (uint32 k = 0; k < C.Cols (); k++)
{
C [j] [k] += B [j] [k];
}
return C;
}
/******************************************************************************/
const real64 kNearZero = 1.0E-10;
/******************************************************************************/
// Work around bug #1294195, which may be a hardware problem on a specific machine.
// This pragma turns on "improved" floating-point consistency.
#ifdef _MSC_VER
#pragma optimize ("p", on)
#endif
static dng_matrix Invert3by3 (const dng_matrix &A)
{
real64 a00 = A [0] [0];
real64 a01 = A [0] [1];
real64 a02 = A [0] [2];
real64 a10 = A [1] [0];
real64 a11 = A [1] [1];
real64 a12 = A [1] [2];
real64 a20 = A [2] [0];
real64 a21 = A [2] [1];
real64 a22 = A [2] [2];
real64 temp [3] [3];
temp [0] [0] = a11 * a22 - a21 * a12;
temp [0] [1] = a21 * a02 - a01 * a22;
temp [0] [2] = a01 * a12 - a11 * a02;
temp [1] [0] = a20 * a12 - a10 * a22;
temp [1] [1] = a00 * a22 - a20 * a02;
temp [1] [2] = a10 * a02 - a00 * a12;
temp [2] [0] = a10 * a21 - a20 * a11;
temp [2] [1] = a20 * a01 - a00 * a21;
temp [2] [2] = a00 * a11 - a10 * a01;
real64 det = (a00 * temp [0] [0] +
a01 * temp [1] [0] +
a02 * temp [2] [0]);
if (Abs_real64 (det) < kNearZero)
{
ThrowMatrixMath ();
}
dng_matrix B (3, 3);
for (uint32 j = 0; j < 3; j++)
for (uint32 k = 0; k < 3; k++)
{
B [j] [k] = temp [j] [k] / det;
}
return B;
}
// Reset floating-point optimization. See comment above.
#ifdef _MSC_VER
#pragma optimize ("p", off)
#endif
/******************************************************************************/
static dng_matrix InvertNbyN (const dng_matrix &A)
{
uint32 i;
uint32 j;
uint32 k;
uint32 n = A.Rows ();
real64 temp [kMaxColorPlanes] [kMaxColorPlanes * 2];
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
temp [i] [j ] = A [i] [j];
temp [i] [j + n] = (i == j ? 1.0 : 0.0);
}
for (i = 0; i < n; i++)
{
real64 alpha = temp [i] [i];
if (Abs_real64 (alpha) < kNearZero)
{
ThrowMatrixMath ();
}
for (j = 0; j < n * 2; j++)
{
temp [i] [j] /= alpha;
}
for (k = 0; k < n; k++)
{
if (i != k)
{
real64 beta = temp [k] [i];
for (j = 0; j < n * 2; j++)
{
temp [k] [j] -= beta * temp [i] [j];
}
}
}
}
dng_matrix B (n, n);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
B [i] [j] = temp [i] [j + n];
}
return B;
}
/******************************************************************************/
dng_matrix Transpose (const dng_matrix &A)
{
dng_matrix B (A.Cols (), A.Rows ());
for (uint32 j = 0; j < B.Rows (); j++)
for (uint32 k = 0; k < B.Cols (); k++)
{
B [j] [k] = A [k] [j];
}
return B;
}
/******************************************************************************/
dng_matrix Invert (const dng_matrix &A)
{
if (A.Rows () < 2 || A.Cols () < 2)
{
ThrowMatrixMath ();
}
if (A.Rows () == A.Cols ())
{
if (A.Rows () == 3)
{
return Invert3by3 (A);
}
return InvertNbyN (A);
}
else
{
// Compute the pseudo inverse.
dng_matrix B = Transpose (A);
return Invert (B * A) * B;
}
}
/******************************************************************************/
dng_matrix Invert (const dng_matrix &A,
const dng_matrix &hint)
{
if (A.Rows () == A .Cols () ||
A.Rows () != hint.Cols () ||
A.Cols () != hint.Rows ())
{
return Invert (A);
}
else
{
// Use the specified hint matrix.
return Invert (hint * A) * hint;
}
}
/*****************************************************************************/