diff options
Diffstat (limited to 'awt/java/awt/color/ICC_ColorSpace.java')
-rw-r--r-- | awt/java/awt/color/ICC_ColorSpace.java | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/awt/java/awt/color/ICC_ColorSpace.java b/awt/java/awt/color/ICC_ColorSpace.java new file mode 100644 index 0000000..5b4d7e9 --- /dev/null +++ b/awt/java/awt/color/ICC_ColorSpace.java @@ -0,0 +1,468 @@ +/* + * 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +import org.apache.harmony.awt.gl.color.ColorConverter; +import org.apache.harmony.awt.gl.color.ColorScaler; +import org.apache.harmony.awt.gl.color.ICC_Transform; +import org.apache.harmony.awt.internal.nls.Messages; + +import java.io.*; + +/** + * This class implements the abstract class ColorSpace and represents device + * independent and device dependent color spaces. This color space is based on + * the International Color Consortium Specification (ICC) File Format for Color + * Profiles: <a href="http://www.color.org">http://www.color.org</a> + * + * @since Android 1.0 + */ +public class ICC_ColorSpace extends ColorSpace { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 3455889114070431483L; + + // Need to keep compatibility with serialized form + /** + * The Constant serialPersistentFields. + */ + private static final ObjectStreamField[] + serialPersistentFields = { + new ObjectStreamField("thisProfile", ICC_Profile.class), //$NON-NLS-1$ + new ObjectStreamField("minVal", float[].class), //$NON-NLS-1$ + new ObjectStreamField("maxVal", float[].class), //$NON-NLS-1$ + new ObjectStreamField("diffMinMax", float[].class), //$NON-NLS-1$ + new ObjectStreamField("invDiffMinMax", float[].class), //$NON-NLS-1$ + new ObjectStreamField("needScaleInit", Boolean.TYPE) //$NON-NLS-1$ + }; + + + /** + * According to ICC specification (from http://www.color.org) "For the + * CIEXYZ encoding, each component (X, Y, and Z) is encoded as a + * u1Fixed15Number". This means that max value for this encoding is 1 + + * (32767/32768) + */ + private static final float MAX_XYZ = 1f + (32767f/32768f); + + /** + * The Constant MAX_SHORT. + */ + private static final float MAX_SHORT = 65535f; + + /** + * The Constant INV_MAX_SHORT. + */ + private static final float INV_MAX_SHORT = 1f/MAX_SHORT; + + /** + * The Constant SHORT2XYZ_FACTOR. + */ + private static final float SHORT2XYZ_FACTOR = MAX_XYZ/MAX_SHORT; + + /** + * The Constant XYZ2SHORT_FACTOR. + */ + private static final float XYZ2SHORT_FACTOR = MAX_SHORT/MAX_XYZ; + + /** + * The profile. + */ + private ICC_Profile profile = null; + + /** + * The min values. + */ + private float minValues[] = null; + + /** + * The max values. + */ + private float maxValues[] = null; + + // cache transforms here - performance gain + /** + * The to rgb transform. + */ + private ICC_Transform toRGBTransform = null; + + /** + * The from rgb transform. + */ + private ICC_Transform fromRGBTransform = null; + + /** + * The to xyz transform. + */ + private ICC_Transform toXYZTransform = null; + + /** + * The from xyz transform. + */ + private ICC_Transform fromXYZTransform = null; + + /** + * The converter. + */ + private final ColorConverter converter = new ColorConverter(); + + /** + * The scaler. + */ + private final ColorScaler scaler = new ColorScaler(); + + /** + * The scaling data loaded. + */ + private boolean scalingDataLoaded = false; + + /** + * The resolved deserialized inst. + */ + private ICC_ColorSpace resolvedDeserializedInst; + + /** + * Instantiates a new ICC color space from an ICC_Profile object. + * + * @param pf + * the ICC_Profile object. + */ + public ICC_ColorSpace(ICC_Profile pf) { + super(pf.getColorSpaceType(), pf.getNumComponents()); + + int pfClass = pf.getProfileClass(); + + switch (pfClass) { + case ICC_Profile.CLASS_COLORSPACECONVERSION: + case ICC_Profile.CLASS_DISPLAY: + case ICC_Profile.CLASS_OUTPUT: + case ICC_Profile.CLASS_INPUT: + break; // OK, it is color conversion profile + default: + // awt.168=Invalid profile class. + throw new IllegalArgumentException(Messages.getString("awt.168")); //$NON-NLS-1$ + } + + profile = pf; + fillMinMaxValues(); + } + + /** + * Gets the ICC_Profile for this ICC_ColorSpace. + * + * @return the ICC_Profile for this ICC_ColorSpace. + */ + public ICC_Profile getProfile() { + if (profile instanceof ICC_ProfileStub) { + profile = ((ICC_ProfileStub) profile).loadProfile(); + } + + return profile; + } + + /** + * Performs the transformation of a color from this ColorSpace into the RGB + * color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the RGB color space. + */ + @Override + public float[] toRGB(float[] colorvalue) { + if (toRGBTransform == null) { + ICC_Profile sRGBProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_sRGB)).getProfile(); + ICC_Profile[] profiles = {getProfile(), sRGBProfile}; + toRGBTransform = new ICC_Transform(profiles); + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + short[] data = new short[getNumComponents()]; + + scaler.scale(colorvalue, data, 0); + + short[] converted = + converter.translateColor(toRGBTransform, data, null); + + // unscale to sRGB + float[] res = new float[3]; + + res[0] = ((converted[0] & 0xFFFF)) * INV_MAX_SHORT; + res[1] = ((converted[1] & 0xFFFF)) * INV_MAX_SHORT; + res[2] = ((converted[2] & 0xFFFF)) * INV_MAX_SHORT; + + return res; + } + + /** + * Performs the transformation of a color from this ColorSpace into the + * CS_CIEXYZ color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the CS_CIEXYZ color + * space. + */ + @Override + public float[] toCIEXYZ(float[] colorvalue) { + if (toXYZTransform == null) { + ICC_Profile xyzProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_CIEXYZ)).getProfile(); + ICC_Profile[] profiles = {getProfile(), xyzProfile}; + try { + int[] intents = { + ICC_Profile.icRelativeColorimetric, + ICC_Profile.icPerceptual}; + toXYZTransform = new ICC_Transform(profiles, intents); + } catch (CMMException e) { // No such tag, use what we can + toXYZTransform = new ICC_Transform(profiles); + } + + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + short[] data = new short[getNumComponents()]; + + scaler.scale(colorvalue, data, 0); + + short[] converted = + converter.translateColor(toXYZTransform, data, null); + + // unscale to XYZ + float[] res = new float[3]; + + res[0] = ((converted[0] & 0xFFFF)) * SHORT2XYZ_FACTOR; + res[1] = ((converted[1] & 0xFFFF)) * SHORT2XYZ_FACTOR; + res[2] = ((converted[2] & 0xFFFF)) * SHORT2XYZ_FACTOR; + + return res; + } + + /** + * Performs the transformation of a color from the RGB color space into this + * ColorSpace. + * + * @param rgbvalue + * the float array representing a color in the RGB color space. + * @return the float array with the transformed color components. + */ + @Override + public float[] fromRGB(float[] rgbvalue) { + if (fromRGBTransform == null) { + ICC_Profile sRGBProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_sRGB)).getProfile(); + ICC_Profile[] profiles = {sRGBProfile, getProfile()}; + fromRGBTransform = new ICC_Transform(profiles); + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + // scale rgb value to short + short[] scaledRGBValue = new short[3]; + scaledRGBValue[0] = (short)(rgbvalue[0] * MAX_SHORT + 0.5f); + scaledRGBValue[1] = (short)(rgbvalue[1] * MAX_SHORT + 0.5f); + scaledRGBValue[2] = (short)(rgbvalue[2] * MAX_SHORT + 0.5f); + + short[] converted = + converter.translateColor(fromRGBTransform, scaledRGBValue, null); + + float[] res = new float[getNumComponents()]; + + scaler.unscale(res, converted, 0); + + return res; + } + + /** + * Performs the transformation of a color from the CS_CIEXYZ color space + * into this ColorSpace. + * + * @param xyzvalue + * the float array representing a color in the CS_CIEXYZ color + * space. + * @return the float array with the transformed color components. + */ + @Override + public float[] fromCIEXYZ(float[] xyzvalue) { + if (fromXYZTransform == null) { + ICC_Profile xyzProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_CIEXYZ)).getProfile(); + ICC_Profile[] profiles = {xyzProfile, getProfile()}; + try { + int[] intents = { + ICC_Profile.icPerceptual, + ICC_Profile.icRelativeColorimetric}; + fromXYZTransform = new ICC_Transform(profiles, intents); + } catch (CMMException e) { // No such tag, use what we can + fromXYZTransform = new ICC_Transform(profiles); + } + + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + + } + + // scale xyz value to short + short[] scaledXYZValue = new short[3]; + scaledXYZValue[0] = (short)(xyzvalue[0] * XYZ2SHORT_FACTOR + 0.5f); + scaledXYZValue[1] = (short)(xyzvalue[1] * XYZ2SHORT_FACTOR + 0.5f); + scaledXYZValue[2] = (short)(xyzvalue[2] * XYZ2SHORT_FACTOR + 0.5f); + + short[] converted = + converter.translateColor(fromXYZTransform, scaledXYZValue, null); + + float[] res = new float[getNumComponents()]; + + scaler.unscale(res, converted, 0); + + return res; + } + + /** + * Gets the minimum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the minimum value. + * @return the minimum normalized value of the component. + */ + @Override + public float getMinValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + // awt.169=Component index out of range + throw new IllegalArgumentException(Messages.getString("awt.169")); //$NON-NLS-1$ + } + + return minValues[component]; + } + + /** + * Gets the maximum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the maximum value. + * @return the maximum normalized value of the component. + */ + @Override + public float getMaxValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + // awt.169=Component index out of range + throw new IllegalArgumentException(Messages.getString("awt.169")); //$NON-NLS-1$ + } + + return maxValues[component]; + } + + /** + * Fill min max values. + */ + private void fillMinMaxValues() { + int n = getNumComponents(); + maxValues = new float[n]; + minValues = new float[n]; + switch (getType()) { + case ColorSpace.TYPE_XYZ: + minValues[0] = 0; + minValues[1] = 0; + minValues[2] = 0; + maxValues[0] = MAX_XYZ; + maxValues[1] = MAX_XYZ; + maxValues[2] = MAX_XYZ; + break; + case ColorSpace.TYPE_Lab: + minValues[0] = 0; + minValues[1] = -128; + minValues[2] = -128; + maxValues[0] = 100; + maxValues[1] = 127; + maxValues[2] = 127; + break; + default: + for(int i=0; i<n; i++) { + minValues[i] = 0; + maxValues[i] = 1; + } + } + } + + /** + * Write object. + * + * @param out + * the out + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + ObjectOutputStream.PutField fields = out.putFields(); + + fields.put("thisProfile", profile); //$NON-NLS-1$ + fields.put("minVal", null); //$NON-NLS-1$ + fields.put("maxVal", null); //$NON-NLS-1$ + fields.put("diffMinMax", null); //$NON-NLS-1$ + fields.put("invDiffMinMax", null); //$NON-NLS-1$ + fields.put("needScaleInit", true); //$NON-NLS-1$ + + out.writeFields(); + } + + /** + * Read object. + * + * @param in + * the in + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws ClassNotFoundException + * the class not found exception + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = in.readFields(); + resolvedDeserializedInst = + new ICC_ColorSpace((ICC_Profile) fields.get("thisProfile", null)); //$NON-NLS-1$ + } + + /** + * Read resolve. + * + * @return the object + * @throws ObjectStreamException + * the object stream exception + */ + Object readResolve() throws ObjectStreamException { + return resolvedDeserializedInst; + } +} + |