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;
021:
022: import java.awt.color.ColorSpace;
023: import java.awt.geom.AffineTransform;
024: import java.awt.geom.Rectangle2D;
025: import java.awt.image.ColorModel;
026: import java.awt.image.DirectColorModel;
027: import java.awt.image.DataBufferInt;
028: import java.awt.image.Raster;
029: import java.awt.image.WritableRaster;
030: import java.io.Serializable;
031: import java.util.Arrays;
032:
033: import org.apache.harmony.awt.internal.nls.Messages;
034:
035: public class Color implements Paint, Serializable {
036: private static final long serialVersionUID = 118526816881161077L;
037:
038: /*
039: * The values of the following colors are based on 1.5 release behavior which
040: * can be revealed using the following or similar code:
041: * Color c = Color.white;
042: * System.out.println(c);
043: */
044:
045: public static final Color white = new Color(255, 255, 255);
046:
047: public static final Color WHITE = white;
048:
049: public static final Color lightGray = new Color(192, 192, 192);
050:
051: public static final Color LIGHT_GRAY = lightGray;
052:
053: public static final Color gray = new Color(128, 128, 128);
054:
055: public static final Color GRAY = gray;
056:
057: public static final Color darkGray = new Color(64, 64, 64);
058:
059: public static final Color DARK_GRAY = darkGray;
060:
061: public static final Color black = new Color(0, 0, 0);
062:
063: public static final Color BLACK = black;
064:
065: public static final Color red = new Color(255, 0, 0);
066:
067: public static final Color RED = red;
068:
069: public static final Color pink = new Color(255, 175, 175);
070:
071: public static final Color PINK = pink;
072:
073: public static final Color orange = new Color(255, 200, 0);
074:
075: public static final Color ORANGE = orange;
076:
077: public static final Color yellow = new Color(255, 255, 0);
078:
079: public static final Color YELLOW = yellow;
080:
081: public static final Color green = new Color(0, 255, 0);
082:
083: public static final Color GREEN = green;
084:
085: public static final Color magenta = new Color(255, 0, 255);
086:
087: public static final Color MAGENTA = magenta;
088:
089: public static final Color cyan = new Color(0, 255, 255);
090:
091: public static final Color CYAN = cyan;
092:
093: public static final Color blue = new Color(0, 0, 255);
094:
095: public static final Color BLUE = blue;
096:
097: /**
098: * integer RGB value
099: */
100: int value;
101:
102: /**
103: * Float sRGB value.
104: */
105: private float[] frgbvalue;
106:
107: /**
108: * Color in an arbitrary color space
109: * with <code>float</code> components.
110: * If null, other value should be used.
111: */
112: private float fvalue[];
113:
114: /**
115: * Float alpha value. If frgbvalue is null, this is not valid data.
116: */
117: private float falpha;
118:
119: /**
120: * The color's color space if applicable.
121: */
122: private ColorSpace cs;
123:
124: /*
125: * The value of the SCALE_FACTOR is based on 1.5 release behavior which
126: * can be revealed using the following code:
127: * Color c = new Color(100, 100, 100);
128: * Color bc = c.brighter();
129: * System.out.println("Brighter factor: " + ((float)c.getRed())/((float)bc.getRed()));
130: * Color dc = c.darker();
131: * System.out.println("Darker factor: " + ((float)dc.getRed())/((float)c.getRed()));
132: * The result is the same for brighter and darker methods, so we need only
133: * one scale factor for both.
134: */
135: private static final double SCALE_FACTOR = 0.7;
136:
137: private static final int MIN_SCALABLE = 3; // should increase when multiplied by SCALE_FACTOR
138:
139: transient private PaintContext currentPaintContext;
140:
141: public Color(ColorSpace cspace, float[] components, float alpha) {
142: int nComps = cspace.getNumComponents();
143: float comp;
144: fvalue = new float[nComps];
145:
146: for (int i = 0; i < nComps; i++) {
147: comp = components[i];
148: if (comp < 0.0f || comp > 1.0f) {
149: // awt.107=Color parameter outside of expected range: component {0}.
150: throw new IllegalArgumentException(Messages.getString(
151: "awt.107", i)); //$NON-NLS-1$
152: }
153: fvalue[i] = components[i];
154: }
155:
156: if (alpha < 0.0f || alpha > 1.0f) {
157: // awt.108=Alpha value outside of expected range.
158: throw new IllegalArgumentException(Messages
159: .getString("awt.108")); //$NON-NLS-1$
160: }
161: falpha = alpha;
162:
163: cs = cspace;
164:
165: frgbvalue = cs.toRGB(fvalue);
166:
167: value = ((int) (frgbvalue[2] * 255 + 0.5))
168: | (((int) (frgbvalue[1] * 255 + 0.5)) << 8)
169: | (((int) (frgbvalue[0] * 255 + 0.5)) << 16)
170: | (((int) (falpha * 255 + 0.5)) << 24);
171: }
172:
173: public Color(int rgba, boolean hasAlpha) {
174: if (!hasAlpha) {
175: value = rgba | 0xFF000000;
176: } else {
177: value = rgba;
178: }
179: }
180:
181: public Color(int r, int g, int b, int a) {
182: if ((r & 0xFF) != r || (g & 0xFF) != g || (b & 0xFF) != b
183: || (a & 0xFF) != a) {
184: // awt.109=Color parameter outside of expected range.
185: throw new IllegalArgumentException(Messages
186: .getString("awt.109")); //$NON-NLS-1$
187: }
188: value = b | (g << 8) | (r << 16) | (a << 24);
189: }
190:
191: public Color(int r, int g, int b) {
192: if ((r & 0xFF) != r || (g & 0xFF) != g || (b & 0xFF) != b) {
193: // awt.109=Color parameter outside of expected range.
194: throw new IllegalArgumentException(Messages
195: .getString("awt.109")); //$NON-NLS-1$
196: }
197: // 0xFF for alpha channel
198: value = b | (g << 8) | (r << 16) | 0xFF000000;
199: }
200:
201: public Color(int rgb) {
202: value = rgb | 0xFF000000;
203: }
204:
205: public Color(float r, float g, float b, float a) {
206: this ((int) (r * 255 + 0.5), (int) (g * 255 + 0.5),
207: (int) (b * 255 + 0.5), (int) (a * 255 + 0.5));
208: falpha = a;
209: fvalue = new float[3];
210: fvalue[0] = r;
211: fvalue[1] = g;
212: fvalue[2] = b;
213: frgbvalue = fvalue;
214: }
215:
216: public Color(float r, float g, float b) {
217: this (r, g, b, 1.0f);
218: }
219:
220: public PaintContext createContext(ColorModel cm, Rectangle r,
221: Rectangle2D r2d, AffineTransform xform, RenderingHints rhs) {
222: if (currentPaintContext != null) {
223: return currentPaintContext;
224: }
225: currentPaintContext = new Color.ColorPaintContext(value);
226: return currentPaintContext;
227: }
228:
229: @Override
230: public String toString() {
231: /*
232: The format of the string is based on 1.5 release behavior which
233: can be revealed using the following code:
234:
235: Color c = new Color(1, 2, 3);
236: System.out.println(c);
237: */
238:
239: return getClass().getName() + "[r=" + getRed() + //$NON-NLS-1$
240: ",g=" + getGreen() + //$NON-NLS-1$
241: ",b=" + getBlue() + //$NON-NLS-1$
242: "]"; //$NON-NLS-1$
243: }
244:
245: @Override
246: public boolean equals(Object obj) {
247: if (obj instanceof Color) {
248: return ((Color) obj).value == this .value;
249: }
250: return false;
251: }
252:
253: public float[] getComponents(ColorSpace colorSpace,
254: float[] components) {
255: int nComps = colorSpace.getNumComponents();
256: if (components == null) {
257: components = new float[nComps + 1];
258: }
259:
260: getColorComponents(colorSpace, components);
261:
262: if (frgbvalue != null) {
263: components[nComps] = falpha;
264: } else {
265: components[nComps] = getAlpha() / 255f;
266: }
267:
268: return components;
269: }
270:
271: public float[] getColorComponents(ColorSpace colorSpace,
272: float[] components) {
273: float[] cieXYZComponents = getColorSpace().toCIEXYZ(
274: getColorComponents(null));
275: float[] csComponents = colorSpace.fromCIEXYZ(cieXYZComponents);
276:
277: if (components == null) {
278: return csComponents;
279: }
280:
281: for (int i = 0; i < csComponents.length; i++) {
282: components[i] = csComponents[i];
283: }
284:
285: return components;
286: }
287:
288: public ColorSpace getColorSpace() {
289: if (cs == null) {
290: cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
291: }
292:
293: return cs;
294: }
295:
296: public Color darker() {
297: return new Color((int) (getRed() * SCALE_FACTOR),
298: (int) (getGreen() * SCALE_FACTOR),
299: (int) (getBlue() * SCALE_FACTOR));
300: }
301:
302: public Color brighter() {
303:
304: int r = getRed();
305: int b = getBlue();
306: int g = getGreen();
307:
308: if (r == 0 && b == 0 && g == 0) {
309: return new Color(MIN_SCALABLE, MIN_SCALABLE, MIN_SCALABLE);
310: }
311:
312: if (r < MIN_SCALABLE && r != 0) {
313: r = MIN_SCALABLE;
314: } else {
315: r = (int) (r / SCALE_FACTOR);
316: r = (r > 255) ? 255 : r;
317: }
318:
319: if (b < MIN_SCALABLE && b != 0) {
320: b = MIN_SCALABLE;
321: } else {
322: b = (int) (b / SCALE_FACTOR);
323: b = (b > 255) ? 255 : b;
324: }
325:
326: if (g < MIN_SCALABLE && g != 0) {
327: g = MIN_SCALABLE;
328: } else {
329: g = (int) (g / SCALE_FACTOR);
330: g = (g > 255) ? 255 : g;
331: }
332:
333: return new Color(r, g, b);
334: }
335:
336: public float[] getRGBComponents(float[] components) {
337: if (components == null) {
338: components = new float[4];
339: }
340:
341: if (frgbvalue != null) {
342: components[3] = falpha;
343: } else {
344: components[3] = getAlpha() / 255f;
345: }
346:
347: getRGBColorComponents(components);
348:
349: return components;
350: }
351:
352: public float[] getRGBColorComponents(float[] components) {
353: if (components == null) {
354: components = new float[3];
355: }
356:
357: if (frgbvalue != null) {
358: components[2] = frgbvalue[2];
359: components[1] = frgbvalue[1];
360: components[0] = frgbvalue[0];
361: } else {
362: components[2] = getBlue() / 255f;
363: components[1] = getGreen() / 255f;
364: components[0] = getRed() / 255f;
365: }
366:
367: return components;
368: }
369:
370: public float[] getComponents(float[] components) {
371: if (fvalue == null) {
372: return getRGBComponents(components);
373: }
374:
375: int nColorComps = fvalue.length;
376:
377: if (components == null) {
378: components = new float[nColorComps + 1];
379: }
380:
381: getColorComponents(components);
382:
383: components[nColorComps] = falpha;
384:
385: return components;
386: }
387:
388: public float[] getColorComponents(float[] components) {
389: if (fvalue == null) {
390: return getRGBColorComponents(components);
391: }
392:
393: if (components == null) {
394: components = new float[fvalue.length];
395: }
396:
397: for (int i = 0; i < fvalue.length; i++) {
398: components[i] = fvalue[i];
399: }
400:
401: return components;
402: }
403:
404: @Override
405: public int hashCode() {
406: return value;
407: }
408:
409: public int getTransparency() {
410: switch (getAlpha()) {
411: case 0xff:
412: return Transparency.OPAQUE;
413: case 0:
414: return Transparency.BITMASK;
415: default:
416: return Transparency.TRANSLUCENT;
417: }
418: }
419:
420: public int getRed() {
421: return (value >> 16) & 0xFF;
422: }
423:
424: public int getRGB() {
425: return value;
426: }
427:
428: public int getGreen() {
429: return (value >> 8) & 0xFF;
430: }
431:
432: public int getBlue() {
433: return value & 0xFF;
434: }
435:
436: public int getAlpha() {
437: return (value >> 24) & 0xFF;
438: }
439:
440: public static Color getColor(String nm, Color def) {
441: Integer integer = Integer.getInteger(nm);
442:
443: if (integer == null) {
444: return def;
445: }
446:
447: return new Color(integer.intValue());
448: }
449:
450: public static Color getColor(String nm, int def) {
451: Integer integer = Integer.getInteger(nm);
452:
453: if (integer == null) {
454: return new Color(def);
455: }
456:
457: return new Color(integer.intValue());
458: }
459:
460: public static Color getColor(String nm) {
461: Integer integer = Integer.getInteger(nm);
462:
463: if (integer == null) {
464: return null;
465: }
466:
467: return new Color(integer.intValue());
468: }
469:
470: public static Color decode(String nm) throws NumberFormatException {
471: Integer integer = Integer.decode(nm);
472: return new Color(integer.intValue());
473: }
474:
475: public static Color getHSBColor(float h, float s, float b) {
476: return new Color(HSBtoRGB(h, s, b));
477: }
478:
479: public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) {
480: if (hsbvals == null) {
481: hsbvals = new float[3];
482: }
483:
484: int V = Math.max(b, Math.max(r, g));
485: int temp = Math.min(b, Math.min(r, g));
486:
487: float H, S, B;
488:
489: B = V / 255.f;
490:
491: if (V == temp) {
492: H = S = 0;
493: } else {
494: S = (V - temp) / ((float) V);
495:
496: float Cr = (V - r) / (float) (V - temp);
497: float Cg = (V - g) / (float) (V - temp);
498: float Cb = (V - b) / (float) (V - temp);
499:
500: if (r == V) {
501: H = Cb - Cg;
502: } else if (g == V) {
503: H = 2 + Cr - Cb;
504: } else {
505: H = 4 + Cg - Cr;
506: }
507:
508: H /= 6.f;
509: if (H < 0) {
510: H++;
511: }
512: }
513:
514: hsbvals[0] = H;
515: hsbvals[1] = S;
516: hsbvals[2] = B;
517:
518: return hsbvals;
519: }
520:
521: public static int HSBtoRGB(float hue, float saturation,
522: float brightness) {
523: float fr, fg, fb;
524:
525: if (saturation == 0) {
526: fr = fg = fb = brightness;
527: } else {
528: float H = (hue - (float) Math.floor(hue)) * 6;
529: int I = (int) Math.floor(H);
530: float F = H - I;
531: float M = brightness * (1 - saturation);
532: float N = brightness * (1 - saturation * F);
533: float K = brightness * (1 - saturation * (1 - F));
534:
535: switch (I) {
536: case 0:
537: fr = brightness;
538: fg = K;
539: fb = M;
540: break;
541: case 1:
542: fr = N;
543: fg = brightness;
544: fb = M;
545: break;
546: case 2:
547: fr = M;
548: fg = brightness;
549: fb = K;
550: break;
551: case 3:
552: fr = M;
553: fg = N;
554: fb = brightness;
555: break;
556: case 4:
557: fr = K;
558: fg = M;
559: fb = brightness;
560: break;
561: case 5:
562: fr = brightness;
563: fg = M;
564: fb = N;
565: break;
566: default:
567: fr = fb = fg = 0; // impossible, to supress compiler error
568: }
569: }
570:
571: int r = (int) (fr * 255. + 0.5);
572: int g = (int) (fg * 255. + 0.5);
573: int b = (int) (fb * 255. + 0.5);
574:
575: return (r << 16) | (g << 8) | b | 0xFF000000;
576: }
577:
578: class ColorPaintContext implements PaintContext {
579: int rgbValue;
580: WritableRaster savedRaster;
581: ColorModel cm;
582:
583: protected ColorPaintContext(int rgb) {
584: rgbValue = rgb;
585: if ((rgb & 0xFF000000) == 0xFF000000) {
586: cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
587: } else {
588: cm = ColorModel.getRGBdefault();
589: }
590: }
591:
592: public void dispose() {
593: savedRaster = null;
594: }
595:
596: public ColorModel getColorModel() {
597: return cm;
598: }
599:
600: public Raster getRaster(int x, int y, int w, int h) {
601: if (savedRaster == null || w != savedRaster.getWidth()
602: || h != savedRaster.getHeight()) {
603: savedRaster = getColorModel()
604: .createCompatibleWritableRaster(w, h);
605:
606: // Suppose we have here simple INT/RGB color/sample model
607: DataBufferInt intBuffer = (DataBufferInt) savedRaster
608: .getDataBuffer();
609: int rgbValues[] = intBuffer.getData();
610: int rgbFillValue = rgbValue;
611: Arrays.fill(rgbValues, rgbFillValue);
612: }
613:
614: return savedRaster;
615: }
616: }
617: }
|