# PDCalRGB.java

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.apache.pdfbox.pdmodel.graphics.color;

    import org.apache.pdfbox.cos.COSArray;
    import org.apache.pdfbox.cos.COSFloat;
    import org.apache.pdfbox.cos.COSName;

    import org.apache.pdfbox.util.Matrix;

    /**
     * A CalRGB colour space is a CIE-based colour space with one transformation stage instead of two.
     * In this type of space, A, B, and C represent calibrated red, green, and blue colour values.
     *
     * @author Ben Litchfield
     * @author John Hewson
     */
    public class PDCalRGB extends PDCIEDictionaryBasedColorSpace
    {
        private final PDColor initialColor = new PDColor(new float[] { 0, 0, 0 }, this);

        /**
         * Creates a new CalRGB color space.
         */
        public PDCalRGB()
        {
            super(COSName.CALRGB);
        }

        /**
         * Creates a new CalRGB color space using the given COS array.
         * @param rgb the cos array which represents this color space
         */
        public PDCalRGB(COSArray rgb)
        {
            super(rgb);
        }

        @Override
        public String getName()
        {
            return COSName.CALRGB.getName();
        }

        @Override
        public int getNumberOfComponents()
        {
            return 3;
        }

        @Override
        public float[] getDefaultDecode(int bitsPerComponent)
        {
            return new float[] { 0, 1, 0, 1, 0, 1 };
        }

        @Override
        public PDColor getInitialColor()
        {
            return initialColor;
        }

        @Override
        public float[] toRGB(float[] value)
        {
            if (isWhitePoint())
            {
                float a = value[0];
                float b = value[1];
                float c = value[2];

                PDGamma gamma = getGamma();
                float powAR = (float)Math.pow(a, gamma.getR());
                float powBG = (float)Math.pow(b, gamma.getG());
                float powCB = (float)Math.pow(c, gamma.getB());

                float[] matrix = getMatrix();
                float mXA = matrix[0];
                float mYA = matrix[1];
                float mZA = matrix[2];
                float mXB = matrix[3];
                float mYB = matrix[4];
                float mZB = matrix[5];
                float mXC = matrix[6];
                float mYC = matrix[7];
                float mZC = matrix[8];

                float x = mXA * powAR + mXB * powBG + mXC * powCB;
                float y = mYA * powAR + mYB * powBG + mYC * powCB;
                float z = mZA * powAR + mZB * powBG + mZC * powCB;
                return convXYZtoRGB(x, y, z);
            }
            else
            {
                // this is a hack, we simply skip CIE calibration of the RGB value
                // this works only with whitepoint D65 (0.9505 1.0 1.089)
                // see PDFBOX-2553
                return new float[] { value[0], value[1], value[2] };
            }
        }

        /**
         * Returns the gamma value.
         * If none is present then the default of 1,1,1 will be returned.
         * @return the gamma value
         */
        public final PDGamma getGamma()
        {
            COSArray gammaArray = dictionary.getCOSArray(COSName.GAMMA);
            if (gammaArray == null)
            {
                gammaArray = new COSArray();
                gammaArray.add(new COSFloat(1.0f));
                gammaArray.add(new COSFloat(1.0f));
                gammaArray.add(new COSFloat(1.0f));
                dictionary.setItem(COSName.GAMMA, gammaArray);
            }
            return new PDGamma(gammaArray);
        }

        /**
         * Returns the linear interpretation matrix, which is an array of nine numbers.
         * If the underlying dictionary contains null then the identity matrix will be returned.
         * @return the linear interpretation matrix
         */
        public final float[] getMatrix()
        {
            COSArray matrix = dictionary.getCOSArray(COSName.MATRIX);
            if (matrix == null)
            {
                return new float[] {  1, 0, 0, 0, 1, 0, 0, 0, 1 };
            }
            else
            {
               return matrix.toFloatArray();
            }
        }

        /**
         * Sets the gamma value.
         * @param gamma the new gamma value
         */
        public final void setGamma(PDGamma gamma)
        {
            COSArray gammaArray = null;
            if(gamma != null)
            {
                gammaArray = gamma.getCOSArray();
            }
            dictionary.setItem(COSName.GAMMA, gammaArray);
        }

        /**
         * Sets the linear interpretation matrix.
         * Passing in null will clear the matrix.
         * @param matrix the new linear interpretation matrix, or null
         */
        public final void setMatrix(Matrix matrix)
        {
            COSArray matrixArray = null;
            if(matrix != null)
            {
                // We can't use matrix.toCOSArray(), as it only returns a subset of the matrix
                float[][] values = matrix.getValues();
                matrixArray = new COSArray();
                matrixArray.add(new COSFloat(values[0][0]));
                matrixArray.add(new COSFloat(values[0][1]));
                matrixArray.add(new COSFloat(values[0][2]));
                matrixArray.add(new COSFloat(values[1][0]));
                matrixArray.add(new COSFloat(values[1][1]));
                matrixArray.add(new COSFloat(values[1][2]));
                matrixArray.add(new COSFloat(values[2][0]));
                matrixArray.add(new COSFloat(values[2][1]));
                matrixArray.add(new COSFloat(values[2][2]));
            }
            dictionary.setItem(COSName.MATRIX, matrixArray);
        }
    }