# PDCIEDictionaryBasedColorSpace.java

    /*
     * Copyright 2014 The Apache Software Foundation.
     *
     * Licensed 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 java.awt.color.ColorSpace;
    import org.apache.pdfbox.cos.COSArray;
    import org.apache.pdfbox.cos.COSDictionary;
    import org.apache.pdfbox.cos.COSFloat;
    import org.apache.pdfbox.cos.COSName;

    /**
     * CIE-based colour spaces that use a dictionary.
     *
     * @author Ben Litchfield
     * @author John Hewson
     */
    public abstract class PDCIEDictionaryBasedColorSpace extends PDCIEBasedColorSpace
    {
        protected final COSDictionary dictionary;

        private static final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);

        // we need to cache whitepoint values, because using getWhitePoint()
        // would create a new default object for each pixel conversion if the original
        // PDF didn't have a whitepoint array
        protected float wpX = 1;
        protected float wpY = 1;
        protected float wpZ = 1;

        protected PDCIEDictionaryBasedColorSpace(COSName cosName)
        {
            array = new COSArray();
            dictionary = new COSDictionary();
            array.add(cosName);
            array.add(dictionary);

            fillWhitepointCache(getWhitepoint());
        }

        /**
         * Creates a new CalRGB color space using the given COS array.
         *
         * @param rgb the cos array which represents this color space
         */
        protected PDCIEDictionaryBasedColorSpace(COSArray rgb)
        {
            array = rgb;
            dictionary = (COSDictionary) array.getObject(1);

            fillWhitepointCache(getWhitepoint());
        }

        /**
         * Tests if the current point is the white point.
         *
         * @return true if the current point is the white point.
         */
        protected boolean isWhitePoint()
        {
            return  Float.compare(wpX, 1) == 0 &&
                    Float.compare(wpY, 1) == 0 &&
                    Float.compare(wpZ, 1)  == 0;
        }

        private void fillWhitepointCache(PDTristimulus whitepoint)
        {
            wpX = whitepoint.getX();
            wpY = whitepoint.getY();
            wpZ = whitepoint.getZ();
        }

        protected float[] convXYZtoRGB(float x, float y, float z)
        {
            // toRGB() malfunctions with negative values
            // XYZ must be non-negative anyway:
            // http://ninedegreesbelow.com/photography/icc-profile-negative-tristimulus.html
            if (x < 0)
            {
                x = 0;
            }
            if (y < 0)
            {
                y = 0;
            }
            if (z < 0)
            {
                z = 0;
            }
            return CIEXYZ.toRGB(new float[]
            {
                x, y, z
            });
        }

        /**
         * This will return the whitepoint tristimulus. As this is a required field
         * this will never return null. A default of 1,1,1 will be returned if the
         * pdf does not have any values yet.
         *
         * @return the whitepoint tristimulus
         */
        public final PDTristimulus getWhitepoint()
        {
            COSArray wp = dictionary.getCOSArray(COSName.WHITE_POINT);
            if (wp == null)
            {
                wp = new COSArray();
                wp.add(new COSFloat(1.0f));
                wp.add(new COSFloat(1.0f));
                wp.add(new COSFloat(1.0f));
            }
            return new PDTristimulus(wp);
        }

        /**
         * This will return the BlackPoint tristimulus. This is an optional field
         * but has defaults so this will never return null. A default of 0,0,0 will
         * be returned if the pdf does not have any values yet.
         *
         * @return the blackpoint tristimulus
         */
        public final PDTristimulus getBlackPoint()
        {
            COSArray bp = dictionary.getCOSArray(COSName.BLACK_POINT);
            if (bp == null)
            {
                bp = new COSArray();
                bp.add(new COSFloat(0.0f));
                bp.add(new COSFloat(0.0f));
                bp.add(new COSFloat(0.0f));
            }
            return new PDTristimulus(bp);
        }

        /**
         * This will set the whitepoint tristimulus. As this is a required field, null should not be
         * passed into this function.
         *
         * @param whitepoint the whitepoint tristimulus.
         * @throws IllegalArgumentException if null is passed as argument.
         */
        public void setWhitePoint(PDTristimulus whitepoint)
        {
            if (whitepoint == null)
            {
                throw new IllegalArgumentException("Whitepoint may not be null");
            }
            dictionary.setItem(COSName.WHITE_POINT, whitepoint);
            fillWhitepointCache(whitepoint);
        }

        /**
         * This will set the BlackPoint tristimulus.
         *
         * @param blackpoint the BlackPoint tristimulus
         */
        public void setBlackPoint(PDTristimulus blackpoint)
        {
            dictionary.setItem(COSName.BLACK_POINT, blackpoint);
        }

    }