/// **************************************************************************
///
/// $Id: MatrixBasedTransformTosRGB.java,v 1.1 2002/07/25 14:56:49 grosbois Exp $
///
/// Copyright Eastman Kodak Company, 343 State Street, Rochester, NY 14650
/// $Date $
/// ***************************************************************************
///
using System;
using ColorSpace = CSJ2K.Color.ColorSpace;
using ICCProfile = CSJ2K.Icc.ICCProfile;
using RestrictedICCProfile = CSJ2K.Icc.RestrictedICCProfile;
using ICCXYZType = CSJ2K.Icc.Tags.ICCXYZType;
using DataBlkInt = CSJ2K.j2k.image.DataBlkInt;
using DataBlkFloat = CSJ2K.j2k.image.DataBlkFloat;
namespace CSJ2K.Icc.Lut
{
/// Transform for applying ICCProfiling to an input DataBlk
///
///
///
///
///
///
/// 1.0
///
/// Bruce A. Kern
///
public class MatrixBasedTransformTosRGB
{
//UPGRADE_NOTE: Final was removed from the declaration of 'eol '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
private static readonly System.String eol = System.Environment.NewLine;
// Start of contant definitions:
//UPGRADE_NOTE: Final was removed from the declaration of 'RED '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
//UPGRADE_NOTE: The initialization of 'RED' was moved to static method 'icc.lut.MatrixBasedTransformTosRGB'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
//UPGRADE_NOTE: Final was removed from the declaration of 'GREEN '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
//UPGRADE_NOTE: The initialization of 'GREEN' was moved to static method 'icc.lut.MatrixBasedTransformTosRGB'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
//UPGRADE_NOTE: Final was removed from the declaration of 'BLUE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
//UPGRADE_NOTE: The initialization of 'BLUE' was moved to static method 'icc.lut.MatrixBasedTransformTosRGB'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
private static readonly int RED;
private static readonly int GREEN;
private static readonly int BLUE;
private const double SRGB00 = 3.1337;
private const double SRGB01 = - 1.6173;
private const double SRGB02 = - 0.4907;
private const double SRGB10 = - 0.9785;
private const double SRGB11 = 1.9162;
private const double SRGB12 = 0.0334;
private const double SRGB20 = 0.0720;
private const double SRGB21 = - 0.2290;
private const double SRGB22 = 1.4056;
// Define constants representing the indices into the matrix array
private const int M00 = 0;
private const int M01 = 1;
private const int M02 = 2;
private const int M10 = 3;
private const int M11 = 4;
private const int M12 = 5;
private const int M20 = 6;
private const int M21 = 7;
private const int M22 = 8;
private const double ksRGBExponent = (1.0 / 2.4);
private const double ksRGBScaleAfterExp = 1.055;
private const double ksRGBReduceAfterExp = 0.055;
private const double ksRGBShadowCutoff = 0.0031308;
private const double ksRGBShadowSlope = 12.92;
// End of contant definitions:
//UPGRADE_NOTE: Final was removed from the declaration of 'matrix '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
private double[] matrix; // Matrix coefficients
private LookUpTableFP[] fLut = new LookUpTableFP[3];
private LookUpTable32LinearSRGBtoSRGB lut; // Linear sRGB to sRGB LUT
//UPGRADE_NOTE: Final was removed from the declaration of 'dwMaxValue '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
private int[] dwMaxValue;
//UPGRADE_NOTE: Final was removed from the declaration of 'dwShiftValue '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
private int[] dwShiftValue;
//private int dwMaxCols = 0; // Maximum number of columns that can be processed
//private int dwMaxRows = 0; // Maximum number of rows that can be processed
private float[][] fBuf = null; // Intermediate output of the first LUT operation.
/// String representation of class
/// suitable representation for class
///
public override System.String ToString()
{
int i, j;
System.Text.StringBuilder rep = new System.Text.StringBuilder("[MatrixBasedTransformTosRGB: ");
System.Text.StringBuilder body = new System.Text.StringBuilder(" ");
body.Append(eol).Append("ksRGBExponent= ").Append(System.Convert.ToString(ksRGBExponent));
body.Append(eol).Append("ksRGBScaleAfterExp= ").Append(System.Convert.ToString(ksRGBScaleAfterExp));
body.Append(eol).Append("ksRGBReduceAfterExp= ").Append(System.Convert.ToString(ksRGBReduceAfterExp));
body.Append(eol).Append("dwMaxValues= ").Append(System.Convert.ToString(dwMaxValue[0])).Append(", ").Append(System.Convert.ToString(dwMaxValue[1])).Append(", ").Append(System.Convert.ToString(dwMaxValue[2]));
body.Append(eol).Append("dwShiftValues= ").Append(System.Convert.ToString(dwShiftValue[0])).Append(", ").Append(System.Convert.ToString(dwShiftValue[1])).Append(", ").Append(System.Convert.ToString(dwShiftValue[2]));
//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Object.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
body.Append(eol).Append(eol).Append("fLut= ").Append(eol).Append(ColorSpace.indent(" ", "fLut[RED]= " + fLut[0].ToString())).Append(eol).Append(ColorSpace.indent(" ", "fLut[GRN]= " + fLut[1].ToString())).Append(eol).Append(ColorSpace.indent(" ", "fLut[BLU]= " + fLut[2].ToString()));
// Print the matrix
body.Append(eol).Append(eol).Append("[matrix ");
for (i = 0; i < 3; ++i)
{
body.Append(eol).Append(" ");
for (j = 0; j < 3; ++j)
{
body.Append(matrix[3 * i + j] + " ");
}
}
body.Append("]");
// Print the LinearSRGBtoSRGB lut.
body.Append(eol).Append(eol).Append(lut.ToString());
rep.Append(ColorSpace.indent(" ", body)).Append("]");
return rep.Append("]").ToString();
}
/// Construct a 3 component transform based on an input RestricedICCProfile
/// This transform will pass the input throught a floating point lut (LookUpTableFP),
/// apply a matrix to the output and finally pass the intermediate buffer through
/// a 8-bit lut (LookUpTable8). This operation will be designated (LFP*M*L8) * Data
/// The operators (LFP*M*L8) are constructed here. Although the data for
/// only one component is returned, the transformation must be done for all
/// components, because the matrix application involves a linear combination of
/// component input to produce the output.
///
/// input profile
///
/// clipping value for output.
///
/// number of columns to transform
///
/// number of rows to transform
///
public MatrixBasedTransformTosRGB(RestrictedICCProfile ricc, int[] dwMaxValue, int[] dwShiftValue)
{
// Assure the proper type profile for this xform.
if (ricc.Type != RestrictedICCProfile.kThreeCompInput)
throw new System.ArgumentException("MatrixBasedTransformTosRGB: wrong type ICCProfile supplied");
int c; // component index.
this.dwMaxValue = dwMaxValue;
this.dwShiftValue = dwShiftValue;
// Create the LUTFP from the input profile.
for (c = 0; c < 3; ++c)
{
fLut[c] = LookUpTableFP.createInstance(ricc.trc[c], dwMaxValue[c] + 1);
}
// Create the Input linear to PCS matrix
matrix = createMatrix(ricc, dwMaxValue); // Create and matrix from the ICC profile.
// Create the final LUT32
lut = LookUpTable32LinearSRGBtoSRGB.createInstance(dwMaxValue[0], dwMaxValue[0], ksRGBShadowCutoff, ksRGBShadowSlope, ksRGBScaleAfterExp, ksRGBExponent, ksRGBReduceAfterExp);
}
private double[] createMatrix(RestrictedICCProfile ricc, int[] maxValues)
{
// Coefficients from the input linear to PCS matrix
double dfPCS00 = ICCXYZType.XYZToDouble(ricc.colorant[RED].x);
double dfPCS01 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].x);
double dfPCS02 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].x);
double dfPCS10 = ICCXYZType.XYZToDouble(ricc.colorant[RED].y);
double dfPCS11 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].y);
double dfPCS12 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].y);
double dfPCS20 = ICCXYZType.XYZToDouble(ricc.colorant[RED].z);
double dfPCS21 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].z);
double dfPCS22 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].z);
double[] matrix = new double[9];
matrix[M00] = maxValues[0] * (SRGB00 * dfPCS00 + SRGB01 * dfPCS10 + SRGB02 * dfPCS20);
matrix[M01] = maxValues[0] * (SRGB00 * dfPCS01 + SRGB01 * dfPCS11 + SRGB02 * dfPCS21);
matrix[M02] = maxValues[0] * (SRGB00 * dfPCS02 + SRGB01 * dfPCS12 + SRGB02 * dfPCS22);
matrix[M10] = maxValues[1] * (SRGB10 * dfPCS00 + SRGB11 * dfPCS10 + SRGB12 * dfPCS20);
matrix[M11] = maxValues[1] * (SRGB10 * dfPCS01 + SRGB11 * dfPCS11 + SRGB12 * dfPCS21);
matrix[M12] = maxValues[1] * (SRGB10 * dfPCS02 + SRGB11 * dfPCS12 + SRGB12 * dfPCS22);
matrix[M20] = maxValues[2] * (SRGB20 * dfPCS00 + SRGB21 * dfPCS10 + SRGB22 * dfPCS20);
matrix[M21] = maxValues[2] * (SRGB20 * dfPCS01 + SRGB21 * dfPCS11 + SRGB22 * dfPCS21);
matrix[M22] = maxValues[2] * (SRGB20 * dfPCS02 + SRGB21 * dfPCS12 + SRGB22 * dfPCS22);
return matrix;
}
/// Performs the transform. Pass the input throught the LookUpTableFP, apply the
/// matrix to the output and finally pass the intermediate buffer through the
/// LookUpTable8. This operation is designated (LFP*M*L8) * Data are already
/// constructed. Although the data for only one component is returned, the
/// transformation must be done for all components, because the matrix application
/// involves a linear combination of component input to produce the output.
///
/// number of columns in the input
///
/// number of rows in the input
///
/// input data block
///
/// output data block
///
///
///
public virtual void apply(DataBlkInt[] inb, DataBlkInt[] outb)
{
int[][] in_Renamed = new int[3][], out_Renamed = new int[3][]; // data references.
int nrows = inb[0].h, ncols = inb[0].w;
if ((fBuf == null) || (fBuf[0].Length < ncols * nrows))
{
float[][] tmpArray = new float[3][];
for (int i = 0; i < 3; i++)
{
tmpArray[i] = new float[ncols * nrows];
}
fBuf = tmpArray;
}
// for each component (rgb)
for (int c = 0; c < 3; ++c)
{
// Reference the input and output samples.
in_Renamed[c] = (int[]) inb[c].Data;
out_Renamed[c] = (int[]) outb[c].Data;
// Assure a properly sized output buffer.
if (out_Renamed[c] == null || out_Renamed[c].Length < in_Renamed[c].Length)
{
out_Renamed[c] = new int[in_Renamed[c].Length];
outb[c].Data = out_Renamed[c];
}
// The first thing to do is to process the input into a standard form
// and through the first input LUT, producing floating point output values
standardizeMatrixLineThroughLut(inb[c], fBuf[c], dwMaxValue[c], fLut[c]);
}
// For each row and column
float[] ra = fBuf[RED];
float[] ga = fBuf[GREEN];
float[] ba = fBuf[BLUE];
int[] ro = out_Renamed[RED];
int[] go = out_Renamed[GREEN];
int[] bo = out_Renamed[BLUE];
int[] lut32 = lut.lut;
double r, g, b;
int val, index = 0;
for (int y = 0; y < inb[0].h; ++y)
{
int end = index + inb[0].w;
while (index < end)
{
// Calculate the rgb pixel indices for this row / column
r = ra[index];
g = ga[index];
b = ba[index];
// Apply the matrix to the intermediate floating point data in order to index the
// final LUT.
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M00] * r + matrix[M01] * g + matrix[M02] * b + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
ro[index] = lut32[0];
else if (val >= lut32.Length)
ro[index] = lut32[lut32.Length - 1];
else
ro[index] = lut32[val];
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M10] * r + matrix[M11] * g + matrix[M12] * b + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
go[index] = lut32[0];
else if (val >= lut32.Length)
go[index] = lut32[lut32.Length - 1];
else
go[index] = lut32[val];
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M20] * r + matrix[M21] * g + matrix[M22] * b + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
bo[index] = lut32[0];
else if (val >= lut32.Length)
bo[index] = lut32[lut32.Length - 1];
else
bo[index] = lut32[val];
index++;
}
}
}
/// Performs the transform. Pass the input throught the LookUpTableFP, apply the
/// matrix to the output and finally pass the intermediate buffer through the
/// LookUpTable8. This operation is designated (LFP*M*L8) * Data are already
/// constructed. Although the data for only one component is returned, the
/// transformation must be done for all components, because the matrix application
/// involves a linear combination of component input to produce the output.
///
/// number of columns in the input
///
/// number of rows in the input
///
/// input data block
///
/// output data block
///
///
///
public virtual void apply(DataBlkFloat[] inb, DataBlkFloat[] outb)
{
float[][] in_Renamed = new float[3][], out_Renamed = new float[3][]; // data references.
int nrows = inb[0].h, ncols = inb[0].w;
if ((fBuf == null) || (fBuf[0].Length < ncols * nrows))
{
float[][] tmpArray = new float[3][];
for (int i = 0; i < 3; i++)
{
tmpArray[i] = new float[ncols * nrows];
}
fBuf = tmpArray;
}
// for each component (rgb)
for (int c = 0; c < 3; ++c)
{
// Reference the input and output pixels.
in_Renamed[c] = (float[]) inb[c].Data;
out_Renamed[c] = (float[]) outb[c].Data;
// Assure a properly sized output buffer.
if (out_Renamed[c] == null || out_Renamed[c].Length < in_Renamed[c].Length)
{
out_Renamed[c] = new float[in_Renamed[c].Length];
outb[c].Data = out_Renamed[c];
}
// The first thing to do is to process the input into a standard form
// and through the first input LUT, producing floating point output values
standardizeMatrixLineThroughLut(inb[c], fBuf[c], dwMaxValue[c], fLut[c]);
}
int[] lut32 = lut.lut;
// For each row and column
int index = 0, val;
for (int y = 0; y < inb[0].h; ++y)
{
int end = index + inb[0].w;
while (index < end)
{
// Calculate the rgb pixel indices for this row / column
// Apply the matrix to the intermediate floating point data inorder to index the
// final LUT.
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M00] * fBuf[RED][index] + matrix[M01] * fBuf[GREEN][index] + matrix[M02] * fBuf[BLUE][index] + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
out_Renamed[0][index] = lut32[0];
else if (val >= lut32.Length)
out_Renamed[0][index] = lut32[lut32.Length - 1];
else
out_Renamed[0][index] = lut32[val];
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M10] * fBuf[RED][index] + matrix[M11] * fBuf[GREEN][index] + matrix[M12] * fBuf[BLUE][index] + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
out_Renamed[1][index] = lut32[0];
else if (val >= lut32.Length)
out_Renamed[1][index] = lut32[lut32.Length - 1];
else
out_Renamed[1][index] = lut32[val];
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
val = (int) (matrix[M20] * fBuf[RED][index] + matrix[M21] * fBuf[GREEN][index] + matrix[M22] * fBuf[BLUE][index] + 0.5);
// Clip the calculated value if necessary..
if (val < 0)
out_Renamed[2][index] = lut32[0];
else if (val >= lut32.Length)
out_Renamed[2][index] = lut32[lut32.Length - 1];
else
out_Renamed[2][index] = lut32[val];
index++;
}
}
}
private static void standardizeMatrixLineThroughLut(DataBlkInt inb, float[] out_Renamed, int dwInputMaxValue, LookUpTableFP lut)
{
int wTemp, j = 0;
int[] in_Renamed = (int[]) inb.Data; // input pixel reference
float[] lutFP = lut.lut;
for (int y = inb.uly; y < inb.uly + inb.h; ++y)
{
for (int x = inb.ulx; x < inb.ulx + inb.w; ++x)
{
int i = inb.offset + (y - inb.uly) * inb.scanw + (x - inb.ulx); // pixel index.
if (in_Renamed[i] > dwInputMaxValue)
wTemp = dwInputMaxValue;
else if (in_Renamed[i] < 0)
wTemp = 0;
else
wTemp = in_Renamed[i];
out_Renamed[j++] = lutFP[wTemp];
}
}
}
private static void standardizeMatrixLineThroughLut(DataBlkFloat inb, float[] out_Renamed, float dwInputMaxValue, LookUpTableFP lut)
{
int j = 0;
float wTemp;
float[] in_Renamed = (float[]) inb.Data; // input pixel reference
float[] lutFP = lut.lut;
for (int y = inb.uly; y < inb.uly + inb.h; ++y)
{
for (int x = inb.ulx; x < inb.ulx + inb.w; ++x)
{
int i = inb.offset + (y - inb.uly) * inb.scanw + (x - inb.ulx); // pixel index.
if (in_Renamed[i] > dwInputMaxValue)
wTemp = dwInputMaxValue;
else if (in_Renamed[i] < 0)
wTemp = 0;
else
wTemp = in_Renamed[i];
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
out_Renamed[j++] = lutFP[(int) wTemp];
}
}
}
/* end class MatrixBasedTransformTosRGB */
static MatrixBasedTransformTosRGB()
{
RED = ICCProfile.RED;
GREEN = ICCProfile.GREEN;
BLUE = ICCProfile.BLUE;
}
}
}