corrade-vassal – Rev 1

Subversion Repositories:
Rev:
/// <summary>**************************************************************************
/// 
/// $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 $
/// ***************************************************************************
/// </summary>
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
{
        
        /// <summary> Transform for applying ICCProfiling to an input DataBlk
        /// 
        /// </summary>
        /// <seealso cref="jj2000.j2k.image.DataBlkInt">
        /// </seealso>
        /// <seealso cref="jj2000.j2k.image.DataBlkFloat">
        /// </seealso>
        /// <version>   1.0
        /// </version>
        /// <author>    Bruce A. Kern
        /// </author>
        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.
                
                /// <summary> String representation of class</summary>
                /// <returns> suitable representation for class 
                /// </returns>
                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();
                }
                
                
                /// <summary> 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.
                /// </summary>
                /// <param name="ricc">input profile
                /// </param>
                /// <param name="dwMaxValue">clipping value for output.
                /// </param>
                /// <param name="dwMaxCols">number of columns to transform
                /// </param>
                /// <param name="dwMaxRows">number of rows to transform
                /// </param>
                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;
                }
                
                
                /// <summary> 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.
                /// </summary>
                /// <param name="ncols">number of columns in the input
                /// </param>
                /// <param name="nrows">number of rows in the input
                /// </param>
                /// <param name="inb">input data block
                /// </param>
                /// <param name="outb">output data block
                /// </param>
                /// <exception cref="MatrixBasedTransformException">
                /// </exception>
                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++;
                                }
                        }
                }
                
                /// <summary> 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.
                /// </summary>
                /// <param name="ncols">number of columns in the input
                /// </param>
                /// <param name="nrows">number of rows in the input
                /// </param>
                /// <param name="inb">input data block
                /// </param>
                /// <param name="outb">output data block
                /// </param>
                /// <exception cref="MatrixBasedTransformException">
                /// </exception>
                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;
                }
        }
}

Generated by GNU Enscript 1.6.5.90.