001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: */package java.awt.color;
021:
022: import org.apache.harmony.awt.gl.color.ColorConverter;
023: import org.apache.harmony.awt.gl.color.ColorScaler;
024: import org.apache.harmony.awt.gl.color.ICC_Transform;
025: import org.apache.harmony.awt.internal.nls.Messages;
026:
027: import java.io.*;
028:
029: public class ICC_ColorSpace extends ColorSpace {
030: private static final long serialVersionUID = 3455889114070431483L;
031:
032: // Need to keep compatibility with serialized form
033: private static final ObjectStreamField[] serialPersistentFields = {
034: new ObjectStreamField("thisProfile", ICC_Profile.class), //$NON-NLS-1$
035: new ObjectStreamField("minVal", float[].class), //$NON-NLS-1$
036: new ObjectStreamField("maxVal", float[].class), //$NON-NLS-1$
037: new ObjectStreamField("diffMinMax", float[].class), //$NON-NLS-1$
038: new ObjectStreamField("invDiffMinMax", float[].class), //$NON-NLS-1$
039: new ObjectStreamField("needScaleInit", Boolean.TYPE) //$NON-NLS-1$
040: };
041:
042: /**
043: * According to ICC specification (from http://www.color.org)
044: * "For the CIEXYZ encoding, each component (X, Y, and Z)
045: * is encoded as a u1Fixed15Number".
046: * This means that max value for this encoding is 1 + (32767/32768)
047: */
048: private static final float MAX_XYZ = 1f + (32767f / 32768f);
049: private static final float MAX_SHORT = 65535f;
050: private static final float INV_MAX_SHORT = 1f / MAX_SHORT;
051: private static final float SHORT2XYZ_FACTOR = MAX_XYZ / MAX_SHORT;
052: private static final float XYZ2SHORT_FACTOR = MAX_SHORT / MAX_XYZ;
053:
054: private ICC_Profile profile = null;
055: private float minValues[] = null;
056: private float maxValues[] = null;
057:
058: // cache transforms here - performance gain
059: private ICC_Transform toRGBTransform = null;
060: private ICC_Transform fromRGBTransform = null;
061: private ICC_Transform toXYZTransform = null;
062: private ICC_Transform fromXYZTransform = null;
063:
064: private final ColorConverter converter = new ColorConverter();
065: private final ColorScaler scaler = new ColorScaler();
066: private boolean scalingDataLoaded = false;
067:
068: private ICC_ColorSpace resolvedDeserializedInst;
069:
070: public ICC_ColorSpace(ICC_Profile pf) {
071: super (pf.getColorSpaceType(), pf.getNumComponents());
072:
073: int pfClass = pf.getProfileClass();
074:
075: switch (pfClass) {
076: case ICC_Profile.CLASS_COLORSPACECONVERSION:
077: case ICC_Profile.CLASS_DISPLAY:
078: case ICC_Profile.CLASS_OUTPUT:
079: case ICC_Profile.CLASS_INPUT:
080: break; // OK, it is color conversion profile
081: default:
082: // awt.168=Invalid profile class.
083: throw new IllegalArgumentException(Messages
084: .getString("awt.168")); //$NON-NLS-1$
085: }
086:
087: profile = pf;
088: fillMinMaxValues();
089: }
090:
091: public ICC_Profile getProfile() {
092: if (profile instanceof ICC_ProfileStub) {
093: profile = ((ICC_ProfileStub) profile).loadProfile();
094: }
095:
096: return profile;
097: }
098:
099: @Override
100: public float[] toRGB(float[] colorvalue) {
101: if (toRGBTransform == null) {
102: ICC_Profile sRGBProfile = ((ICC_ColorSpace) ColorSpace
103: .getInstance(CS_sRGB)).getProfile();
104: ICC_Profile[] profiles = { getProfile(), sRGBProfile };
105: toRGBTransform = new ICC_Transform(profiles);
106: if (!scalingDataLoaded) {
107: scaler.loadScalingData(this );
108: scalingDataLoaded = true;
109: }
110: }
111:
112: short[] data = new short[getNumComponents()];
113:
114: scaler.scale(colorvalue, data, 0);
115:
116: short[] converted = converter.translateColor(toRGBTransform,
117: data, null);
118:
119: // unscale to sRGB
120: float[] res = new float[3];
121:
122: res[0] = ((converted[0] & 0xFFFF)) * INV_MAX_SHORT;
123: res[1] = ((converted[1] & 0xFFFF)) * INV_MAX_SHORT;
124: res[2] = ((converted[2] & 0xFFFF)) * INV_MAX_SHORT;
125:
126: return res;
127: }
128:
129: @Override
130: public float[] toCIEXYZ(float[] colorvalue) {
131: if (toXYZTransform == null) {
132: ICC_Profile xyzProfile = ((ICC_ColorSpace) ColorSpace
133: .getInstance(CS_CIEXYZ)).getProfile();
134: ICC_Profile[] profiles = { getProfile(), xyzProfile };
135: try {
136: int[] intents = { ICC_Profile.icRelativeColorimetric,
137: ICC_Profile.icPerceptual };
138: toXYZTransform = new ICC_Transform(profiles, intents);
139: } catch (CMMException e) { // No such tag, use what we can
140: toXYZTransform = new ICC_Transform(profiles);
141: }
142:
143: if (!scalingDataLoaded) {
144: scaler.loadScalingData(this );
145: scalingDataLoaded = true;
146: }
147: }
148:
149: short[] data = new short[getNumComponents()];
150:
151: scaler.scale(colorvalue, data, 0);
152:
153: short[] converted = converter.translateColor(toXYZTransform,
154: data, null);
155:
156: // unscale to XYZ
157: float[] res = new float[3];
158:
159: res[0] = ((converted[0] & 0xFFFF)) * SHORT2XYZ_FACTOR;
160: res[1] = ((converted[1] & 0xFFFF)) * SHORT2XYZ_FACTOR;
161: res[2] = ((converted[2] & 0xFFFF)) * SHORT2XYZ_FACTOR;
162:
163: return res;
164: }
165:
166: @Override
167: public float[] fromRGB(float[] rgbvalue) {
168: if (fromRGBTransform == null) {
169: ICC_Profile sRGBProfile = ((ICC_ColorSpace) ColorSpace
170: .getInstance(CS_sRGB)).getProfile();
171: ICC_Profile[] profiles = { sRGBProfile, getProfile() };
172: fromRGBTransform = new ICC_Transform(profiles);
173: if (!scalingDataLoaded) {
174: scaler.loadScalingData(this );
175: scalingDataLoaded = true;
176: }
177: }
178:
179: // scale rgb value to short
180: short[] scaledRGBValue = new short[3];
181: scaledRGBValue[0] = (short) (rgbvalue[0] * MAX_SHORT + 0.5f);
182: scaledRGBValue[1] = (short) (rgbvalue[1] * MAX_SHORT + 0.5f);
183: scaledRGBValue[2] = (short) (rgbvalue[2] * MAX_SHORT + 0.5f);
184:
185: short[] converted = converter.translateColor(fromRGBTransform,
186: scaledRGBValue, null);
187:
188: float[] res = new float[getNumComponents()];
189:
190: scaler.unscale(res, converted, 0);
191:
192: return res;
193: }
194:
195: @Override
196: public float[] fromCIEXYZ(float[] xyzvalue) {
197: if (fromXYZTransform == null) {
198: ICC_Profile xyzProfile = ((ICC_ColorSpace) ColorSpace
199: .getInstance(CS_CIEXYZ)).getProfile();
200: ICC_Profile[] profiles = { xyzProfile, getProfile() };
201: try {
202: int[] intents = { ICC_Profile.icPerceptual,
203: ICC_Profile.icRelativeColorimetric };
204: fromXYZTransform = new ICC_Transform(profiles, intents);
205: } catch (CMMException e) { // No such tag, use what we can
206: fromXYZTransform = new ICC_Transform(profiles);
207: }
208:
209: if (!scalingDataLoaded) {
210: scaler.loadScalingData(this );
211: scalingDataLoaded = true;
212: }
213: }
214:
215: // scale xyz value to short
216: short[] scaledXYZValue = new short[3];
217: scaledXYZValue[0] = (short) (xyzvalue[0] * XYZ2SHORT_FACTOR + 0.5f);
218: scaledXYZValue[1] = (short) (xyzvalue[1] * XYZ2SHORT_FACTOR + 0.5f);
219: scaledXYZValue[2] = (short) (xyzvalue[2] * XYZ2SHORT_FACTOR + 0.5f);
220:
221: short[] converted = converter.translateColor(fromXYZTransform,
222: scaledXYZValue, null);
223:
224: float[] res = new float[getNumComponents()];
225:
226: scaler.unscale(res, converted, 0);
227:
228: return res;
229: }
230:
231: @Override
232: public float getMinValue(int component) {
233: if ((component < 0)
234: || (component > this .getNumComponents() - 1)) {
235: // awt.169=Component index out of range
236: throw new IllegalArgumentException(Messages
237: .getString("awt.169")); //$NON-NLS-1$
238: }
239:
240: return minValues[component];
241: }
242:
243: @Override
244: public float getMaxValue(int component) {
245: if ((component < 0)
246: || (component > this .getNumComponents() - 1)) {
247: // awt.169=Component index out of range
248: throw new IllegalArgumentException(Messages
249: .getString("awt.169")); //$NON-NLS-1$
250: }
251:
252: return maxValues[component];
253: }
254:
255: private void fillMinMaxValues() {
256: int n = getNumComponents();
257: maxValues = new float[n];
258: minValues = new float[n];
259: switch (getType()) {
260: case ColorSpace.TYPE_XYZ:
261: minValues[0] = 0;
262: minValues[1] = 0;
263: minValues[2] = 0;
264: maxValues[0] = MAX_XYZ;
265: maxValues[1] = MAX_XYZ;
266: maxValues[2] = MAX_XYZ;
267: break;
268: case ColorSpace.TYPE_Lab:
269: minValues[0] = 0;
270: minValues[1] = -128;
271: minValues[2] = -128;
272: maxValues[0] = 100;
273: maxValues[1] = 127;
274: maxValues[2] = 127;
275: break;
276: default:
277: for (int i = 0; i < n; i++) {
278: minValues[i] = 0;
279: maxValues[i] = 1;
280: }
281: }
282: }
283:
284: private void writeObject(ObjectOutputStream out) throws IOException {
285: ObjectOutputStream.PutField fields = out.putFields();
286:
287: fields.put("thisProfile", profile); //$NON-NLS-1$
288: fields.put("minVal", null); //$NON-NLS-1$
289: fields.put("maxVal", null); //$NON-NLS-1$
290: fields.put("diffMinMax", null); //$NON-NLS-1$
291: fields.put("invDiffMinMax", null); //$NON-NLS-1$
292: fields.put("needScaleInit", true); //$NON-NLS-1$
293:
294: out.writeFields();
295: }
296:
297: private void readObject(ObjectInputStream in) throws IOException,
298: ClassNotFoundException {
299: ObjectInputStream.GetField fields = in.readFields();
300: resolvedDeserializedInst = new ICC_ColorSpace(
301: (ICC_Profile) fields.get("thisProfile", null)); //$NON-NLS-1$
302: }
303:
304: Object readResolve() throws ObjectStreamException {
305: return resolvedDeserializedInst;
306: }
307: }
|