001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.utils;
031:
032: import java.awt.Color;
033: import java.awt.Component;
034:
035: import javax.swing.AbstractButton;
036: import javax.swing.ButtonModel;
037:
038: import org.jvnet.lafwidget.animation.FadeKind;
039: import org.jvnet.lafwidget.animation.FadeState;
040: import org.jvnet.substance.SubstanceLookAndFeel;
041: import org.jvnet.substance.color.ColorScheme;
042: import org.jvnet.substance.color.ColorBlindColorScheme.BlindnessKind;
043: import org.jvnet.substance.theme.SubstanceComplexTheme;
044: import org.jvnet.substance.theme.SubstanceTheme;
045:
046: /**
047: * Various color-related utilities. This class is <b>for internal use only</b>.
048: *
049: * @author Kirill Grouchnikov
050: */
051: public class SubstanceColorUtilities {
052: /**
053: * Returns the color of the top portion of border in control backgrounds.
054: *
055: * @param scheme1
056: * The first color scheme.
057: * @param scheme2
058: * The second color scheme.
059: * @param cycleCoef
060: * Cycle position. Is used for rollover and pulsation effects.
061: * Must be in 0.0 .. 1.0 range.
062: * @return The color of the top portion of border in control backgrounds.
063: */
064: public static Color getTopBorderColor(ColorScheme scheme1,
065: ColorScheme scheme2, double cycleCoef) {
066: return SubstanceColorUtilities.getInterpolatedColor(scheme1
067: .getUltraDarkColor(), scheme2.getUltraDarkColor(),
068: cycleCoef);
069: }
070:
071: /**
072: * Returns the color of the middle portion of border in control backgrounds.
073: *
074: * @param scheme1
075: * The first color scheme.
076: * @param scheme2
077: * The second color scheme.
078: * @param cycleCoef
079: * Cycle position. Is used for rollover and pulsation effects.
080: * Must be in 0.0 .. 1.0 range.
081: * @return The color of the middle portion of border in control backgrounds.
082: */
083: public static Color getMidBorderColor(ColorScheme scheme1,
084: ColorScheme scheme2, double cycleCoef) {
085: // if (isDark) {
086: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
087: // .getExtraLightColor(), scheme2.getExtraLightColor(),
088: // cycleCoef);
089: // } else {
090: return SubstanceColorUtilities.getInterpolatedColor(scheme1
091: .getDarkColor(), scheme2.getDarkColor(), cycleCoef);
092: // }
093: }
094:
095: /**
096: * Returns the color of the bottom portion of border in control backgrounds.
097: *
098: * @param scheme1
099: * The first color scheme.
100: * @param scheme2
101: * The second color scheme.
102: * @param cycleCoef
103: * Cycle position. Is used for rollover and pulsation effects.
104: * Must be in 0.0 .. 1.0 range.
105: * @return The color of the bottom portion of border in control backgrounds.
106: */
107: public static Color getBottomBorderColor(ColorScheme scheme1,
108: ColorScheme scheme2, double cycleCoef) {
109: // if (isDark) {
110: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
111: // .getLightColor(), scheme2.getLightColor(), cycleCoef);
112: // } else {
113: Color c1 = SubstanceColorUtilities.getInterpolatedColor(scheme1
114: .getDarkColor(), scheme1.getMidColor(), 0.5);
115: Color c2 = SubstanceColorUtilities.getInterpolatedColor(scheme2
116: .getDarkColor(), scheme2.getMidColor(), 0.5);
117: return SubstanceColorUtilities.getInterpolatedColor(c1, c2,
118: cycleCoef);
119: // }
120: }
121:
122: /**
123: * Returns the color of the top portion of fill in control backgrounds.
124: *
125: * @param scheme1
126: * The first color scheme.
127: * @param scheme2
128: * The second color scheme.
129: * @param cycleCoef
130: * Cycle position. Is used for rollover and pulsation effects.
131: * Must be in 0.0 .. 1.0 range.
132: * @param useCyclePosAsInterpolation
133: * Indicates the algorithm to use for computing various colors.
134: * If <code>true</code>, the <code>cyclePos</code> is used
135: * to interpolate colors between different color components of
136: * both color schemes. If <code>false</code>, the
137: * <code>cyclePos</code> is used to interpolate colors between
138: * different color components of the first color scheme.
139: * @return The color of the top portion of fill in control backgrounds.
140: */
141: public static Color getTopFillColor(ColorScheme scheme1,
142: ColorScheme scheme2, double cycleCoef,
143: boolean useCyclePosAsInterpolation) {
144: if (!useCyclePosAsInterpolation) {
145: Color c = SubstanceColorUtilities.getInterpolatedColor(
146: scheme1.getDarkColor(), scheme1.getMidColor(), 0.4);
147: return SubstanceColorUtilities.getInterpolatedColor(c,
148: scheme2.getLightColor(), cycleCoef);
149: } else {
150: Color c1 = SubstanceColorUtilities.getInterpolatedColor(
151: scheme1.getDarkColor(), scheme1.getMidColor(), 0.4);
152: Color c2 = SubstanceColorUtilities.getInterpolatedColor(
153: scheme2.getDarkColor(), scheme2.getMidColor(), 0.4);
154: return SubstanceColorUtilities.getInterpolatedColor(c1, c2,
155: cycleCoef);
156: }
157: }
158:
159: /**
160: * Returns the color of the middle portion of fill in control backgrounds.
161: *
162: * @param scheme1
163: * The first color scheme.
164: * @param scheme2
165: * The second color scheme.
166: * @param cycleCoef
167: * Cycle position. Is used for rollover and pulsation effects.
168: * Must be in 0.0 .. 1.0 range.
169: * @param useCyclePosAsInterpolation
170: * Indicates the algorithm to use for computing various colors.
171: * If <code>true</code>, the <code>cyclePos</code> is used
172: * to interpolate colors between different color components of
173: * both color schemes. If <code>false</code>, the
174: * <code>cyclePos</code> is used to interpolate colors between
175: * different color components of the first color scheme.
176: * @return The color of the middle portion of fill in control backgrounds.
177: */
178: public static Color getMidFillColor(ColorScheme scheme1,
179: ColorScheme scheme2, double cycleCoef,
180: boolean useCyclePosAsInterpolation) {
181: if (!useCyclePosAsInterpolation) {
182: return SubstanceColorUtilities.getInterpolatedColor(scheme1
183: .getMidColor(), scheme2.getLightColor(), cycleCoef);
184: } else {
185: return SubstanceColorUtilities.getInterpolatedColor(scheme1
186: .getMidColor(), scheme2.getMidColor(), cycleCoef);
187: }
188: }
189:
190: /**
191: * Returns the color of the bottom portion of fill in control backgrounds.
192: *
193: * @param scheme1
194: * The first color scheme.
195: * @param scheme2
196: * The second color scheme.
197: * @param cycleCoef
198: * Cycle position. Is used for rollover and pulsation effects.
199: * Must be in 0.0 .. 1.0 range.
200: * @param useCyclePosAsInterpolation
201: * Indicates the algorithm to use for computing various colors.
202: * If <code>true</code>, the <code>cyclePos</code> is used
203: * to interpolate colors between different color components of
204: * both color schemes. If <code>false</code>, the
205: * <code>cyclePos</code> is used to interpolate colors between
206: * different color components of the first color scheme.
207: * @return The color of the bottom portion of fill in control backgrounds.
208: */
209: public static Color getBottomFillColor(ColorScheme scheme1,
210: ColorScheme scheme2, double cycleCoef,
211: boolean useCyclePosAsInterpolation) {
212: // if (isDark) {
213: // if (!useCyclePosAsInterpolation) {
214: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
215: // .getDarkColor(), scheme2.getMidColor(), cycleCoef);
216: // } else {
217: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
218: // .getDarkColor(), scheme2.getDarkColor(), cycleCoef);
219: // }
220: // } else {
221: if (!useCyclePosAsInterpolation) {
222: return SubstanceColorUtilities.getInterpolatedColor(scheme1
223: .getUltraLightColor(),
224: scheme2.getExtraLightColor(), cycleCoef);
225: } else {
226: return SubstanceColorUtilities.getInterpolatedColor(scheme1
227: .getUltraLightColor(),
228: scheme2.getUltraLightColor(), cycleCoef);
229: }
230: // }
231: }
232:
233: /**
234: * Returns the color of the top portion of shine in control backgrounds.
235: *
236: * @param scheme1
237: * The first color scheme.
238: * @param scheme2
239: * The second color scheme.
240: * @param cycleCoef
241: * Cycle position. Is used for rollover and pulsation effects.
242: * Must be in 0.0 .. 1.0 range.
243: * @param useCyclePosAsInterpolation
244: * Indicates the algorithm to use for computing various colors.
245: * If <code>true</code>, the <code>cyclePos</code> is used
246: * to interpolate colors between different color components of
247: * both color schemes. If <code>false</code>, the
248: * <code>cyclePos</code> is used to interpolate colors between
249: * different color components of the first color scheme.
250: * @return The color of the top portion of shine in control backgrounds.
251: */
252: public static Color getTopShineColor(ColorScheme scheme1,
253: ColorScheme scheme2, double cycleCoef,
254: boolean useCyclePosAsInterpolation) {
255: return getBottomFillColor(scheme1, scheme2, cycleCoef,
256: useCyclePosAsInterpolation);
257: }
258:
259: /**
260: * Returns the color of the bottom portion of shine in control backgrounds.
261: *
262: * @param scheme1
263: * The first color scheme.
264: * @param scheme2
265: * The second color scheme.
266: * @param cycleCoef
267: * Cycle position. Is used for rollover and pulsation effects.
268: * Must be in 0.0 .. 1.0 range.
269: * @param useCyclePosAsInterpolation
270: * Indicates the algorithm to use for computing various colors.
271: * If <code>true</code>, the <code>cyclePos</code> is used
272: * to interpolate colors between different color components of
273: * both color schemes. If <code>false</code>, the
274: * <code>cyclePos</code> is used to interpolate colors between
275: * different color components of the first color scheme.
276: * @return The color of the bottom portion of shine in control backgrounds.
277: */
278: public static Color getBottomShineColor(ColorScheme scheme1,
279: ColorScheme scheme2, double cycleCoef,
280: boolean useCyclePosAsInterpolation) {
281: // if (isDark) {
282: // if (!useCyclePosAsInterpolation) {
283: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
284: // .getMidColor(), scheme2.getDarkColor(), cycleCoef);
285: // } else {
286: // return SubstanceCoreUtilities.getInterpolatedColor(scheme1
287: // .getMidColor(), scheme2.getMidColor(), cycleCoef);
288: // }
289: // } else {
290: if (!useCyclePosAsInterpolation) {
291: return SubstanceColorUtilities.getInterpolatedColor(scheme1
292: .getLightColor(), scheme2.getUltraLightColor(),
293: cycleCoef);
294: } else {
295: return SubstanceColorUtilities.getInterpolatedColor(scheme1
296: .getLightColor(), scheme2.getLightColor(),
297: cycleCoef);
298: }
299: // }
300: }
301:
302: /**
303: * Interpolates color.
304: *
305: * @param color1
306: * The first color
307: * @param color2
308: * The second color
309: * @param color1Likeness
310: * The closer this value is to 0.0, the closer the resulting
311: * color will be to <code>color2</code>.
312: * @return Interpolated RGB value.
313: */
314: public static int getInterpolatedRGB(Color color1, Color color2,
315: double color1Likeness) {
316: int lr = color1.getRed();
317: int lg = color1.getGreen();
318: int lb = color1.getBlue();
319: int la = color1.getAlpha();
320: int dr = color2.getRed();
321: int dg = color2.getGreen();
322: int db = color2.getBlue();
323: int da = color2.getAlpha();
324:
325: int r = (int) (color1Likeness * lr + (1.0 - color1Likeness)
326: * dr);
327: int g = (int) (color1Likeness * lg + (1.0 - color1Likeness)
328: * dg);
329: int b = (int) (color1Likeness * lb + (1.0 - color1Likeness)
330: * db);
331: int a = (int) (color1Likeness * la + (1.0 - color1Likeness)
332: * da);
333:
334: r = Math.min(255, r);
335: g = Math.min(255, g);
336: b = Math.min(255, b);
337: a = Math.min(255, a);
338: r = Math.max(0, r);
339: g = Math.max(0, g);
340: b = Math.max(0, b);
341: a = Math.max(0, a);
342: return (a << 24) | (r << 16) | (g << 8) | b;
343: }
344:
345: /**
346: * Interpolates color.
347: *
348: * @param color1
349: * The first color
350: * @param color2
351: * The second color
352: * @param color1Likeness
353: * The closer this value is to 0.0, the closer the resulting
354: * color will be to <code>color2</code>.
355: * @return Interpolated color.
356: */
357: public static Color getInterpolatedColor(Color color1,
358: Color color2, double color1Likeness) {
359: if (color1.equals(color2))
360: return color1;
361: if (color1Likeness == 1.0)
362: return color1;
363: if (color1Likeness == 0.0)
364: return color2;
365: return new Color(getInterpolatedRGB(color1, color2,
366: color1Likeness), true);
367: }
368:
369: /**
370: * Inverts the specified color.
371: *
372: * @param color
373: * The original color.
374: * @return The inverted color.
375: */
376: public static Color invertColor(Color color) {
377: return new Color(255 - color.getRed(), 255 - color.getGreen(),
378: 255 - color.getBlue(), color.getAlpha());
379: }
380:
381: /**
382: * Returns background color for the specified theme.
383: *
384: * @param theme
385: * Theme.
386: * @return Background color for the specified theme.
387: */
388: public static Color getBackgroundColor(SubstanceTheme theme) {
389: if (theme instanceof SubstanceComplexTheme) {
390: SubstanceComplexTheme sct = (SubstanceComplexTheme) theme;
391: SubstanceTheme defTheme = sct.getDefaultTheme();
392: switch (defTheme.getKind()) {
393: case DARK:
394: return defTheme.getColorScheme().getDarkColor()
395: .brighter();
396: default:
397: return defTheme.getColorScheme().getExtraLightColor();
398: }
399: }
400: switch (theme.getKind()) {
401: case DARK:
402: return theme.getColorScheme().getDarkColor().brighter();
403: default:
404: return theme.getDefaultColorScheme().getExtraLightColor();
405: // SubstanceTheme.getMetallic(theme.getKind())
406: // .getExtraLightColor();
407: }
408: }
409:
410: /**
411: * Returns disabled foreground color for the specified theme.
412: *
413: * @param theme
414: * Theme.
415: * @return Disabled foreground color for the specified theme.
416: */
417: public static Color getDisabledForegroundColor(SubstanceTheme theme) {
418: return theme.getDisabledTheme().getForegroundColor();
419: // switch (theme.getKind()) {
420: // case DARK:
421: // return getInterpolatedColor(theme.getDefaultColorScheme()
422: // .getForegroundColor(), theme.getColorScheme()
423: // .getDarkColor(), 0.45);
424: // default:
425: // return getInterpolatedColor(theme.getDefaultColorScheme()
426: // .getMidColor(), theme.getDefaultColorScheme()
427: // .getDarkColor(), 0.7);
428: // }
429: }
430:
431: /**
432: * Returns selection background color for the specified theme.
433: *
434: * @param theme
435: * Theme.
436: * @return Selection background color for the specified theme.
437: */
438: public static Color getSelectionBackgroundColor(SubstanceTheme theme) {
439: // fix for enhancement 182 - use highlight theme as
440: // selection background.
441: switch (theme.getKind()) {
442: case DARK:
443: return theme.getHighlightTheme(null, ComponentState.ACTIVE)
444: .getColorScheme().getUltraLightColor().brighter();
445: // .brighter();
446: default:
447: return theme.getHighlightTheme(null, ComponentState.ACTIVE)
448: .getColorScheme().getExtraLightColor();
449: }
450:
451: }
452:
453: /**
454: * Returns line color for the specified theme.
455: *
456: * @param theme
457: * Theme.
458: * @return Line color for the specified theme.
459: */
460: public static Color getLineColor(SubstanceTheme theme) {
461: switch (theme.getKind()) {
462: case DARK:
463: return theme.getColorScheme().getUltraLightColor();
464: default:
465: return theme.getColorScheme().getMidColor();
466: }
467: }
468:
469: /**
470: * Returns selection foreground color for the specified theme.
471: *
472: * @param theme
473: * Theme.
474: * @return Selection foreground color for the specified theme.
475: */
476: public static Color getSelectionForegroundColor(SubstanceTheme theme) {
477: switch (theme.getKind()) {
478: case DARK:
479: return theme.getActiveTheme().getColorScheme()
480: .getUltraDarkColor().darker();
481: default:
482: return theme.getActiveTheme().getColorScheme()
483: .getUltraDarkColor();
484: }
485: }
486:
487: /**
488: * Returns the watermark stamp color for the current theme.
489: *
490: * @return Watermark stamp color for the current theme.
491: */
492: public static Color getWatermarkStampColor() {
493: return getWatermarkStampColor(SubstanceLookAndFeel.getTheme());
494: }
495:
496: /**
497: * Returns the watermark stamp color for the current theme.
498: *
499: * @param theme
500: * Theme.
501: * @return Watermark stamp color for the current theme.
502: */
503: public static Color getWatermarkStampColor(SubstanceTheme theme) {
504: return theme.getWatermarkTheme().getWatermarkStampColor();
505: }
506:
507: /**
508: * Returns a negative of the specified color.
509: *
510: * @param color
511: * Color.
512: * @return Negative of the specified color.
513: */
514: public static Color getNegativeColor(Color color) {
515: return new Color(255 - color.getRed(), 255 - color.getGreen(),
516: 255 - color.getBlue(), color.getAlpha());
517: }
518:
519: /**
520: * Returns a negative of the specified color.
521: *
522: * @param rgb
523: * Color RGB.
524: * @return Negative of the specified color.
525: */
526: public static int getNegativeColor(int rgb) {
527: int transp = (rgb >>> 24) & 0xFF;
528: int r = (rgb >>> 16) & 0xFF;
529: int g = (rgb >>> 8) & 0xFF;
530: int b = (rgb >>> 0) & 0xFF;
531:
532: return (transp << 24) | ((255 - r) << 16) | ((255 - g) << 8)
533: | (255 - b);
534: }
535:
536: /**
537: * Returns the watermark light color for the current theme.
538: *
539: * @return Watermark light color for the current theme.
540: */
541: public static Color getWatermarkLightColor() {
542: return getWatermarkLightColor(1.0f);
543: }
544:
545: /**
546: * Returns the watermark light color for the current theme.
547: *
548: * @param theme
549: * Theme.
550: * @return Watermark light color for the current theme.
551: */
552: public static Color getWatermarkLightColor(SubstanceTheme theme) {
553: return getWatermarkLightColor(theme, 1.0f);
554: }
555:
556: /**
557: * Returns the watermark light color for the current theme.
558: *
559: * @param coef
560: * Translucency coefficient.
561: * @return Watermark light color for the current theme.
562: */
563: public static Color getWatermarkLightColor(float coef) {
564: return getWatermarkLightColor(SubstanceLookAndFeel.getTheme(),
565: coef);
566: }
567:
568: /**
569: * Returns the watermark light color for the current theme.
570: *
571: * @param theme
572: * Theme.
573: * @param coef
574: * Translucency coefficient.
575: * @return Watermark light color for the current theme.
576: */
577: public static Color getWatermarkLightColor(SubstanceTheme theme,
578: float coef) {
579: SubstanceTheme watermarkTheme = theme.getWatermarkTheme();
580: int alpha = SubstanceCoreUtilities.isThemeDark(watermarkTheme) ? (int) (coef * 100)
581: : (int) (coef * 255);
582: if (alpha > 255)
583: alpha = 255;
584: return SubstanceColorUtilities.getAlphaColor(watermarkTheme
585: .getColorScheme().getLightColor(), alpha);
586: }
587:
588: /**
589: * Returns the watermark dark color for the current theme.
590: *
591: * @return Watermark dark color for the current theme.
592: */
593: public static Color getWatermarkDarkColor() {
594: return getWatermarkDarkColor(SubstanceLookAndFeel.getTheme());
595: }
596:
597: /**
598: * Returns the watermark dark color for the current theme.
599: *
600: * @param theme
601: * Theme.
602: * @return Watermark dark color for the current theme.
603: */
604: public static Color getWatermarkDarkColor(SubstanceTheme theme) {
605: SubstanceTheme watermarkTheme = theme.getWatermarkTheme();
606: return SubstanceCoreUtilities.isThemeDark(watermarkTheme) ? SubstanceColorUtilities
607: .getAlphaColor(watermarkTheme.getColorScheme()
608: .getUltraLightColor(), 128)
609: : SubstanceColorUtilities.getAlphaColor(watermarkTheme
610: .getColorScheme().getDarkColor(), 15);
611: }
612:
613: /**
614: * Returns the watermark stamp color of the specified opacity for the
615: * current theme.
616: *
617: * @param alpha
618: * Alpha channel value.
619: * @return Watermark stamp color of the specified opacity for the current
620: * theme.
621: */
622: public static Color getWatermarkStampColor(int alpha) {
623: SubstanceTheme watermarkTheme = SubstanceLookAndFeel.getTheme()
624: .getWatermarkTheme();
625: return SubstanceCoreUtilities.isThemeDark(watermarkTheme) ? SubstanceColorUtilities
626: .getAlphaColor(watermarkTheme.getColorScheme()
627: .getUltraLightColor(), alpha)
628: : SubstanceColorUtilities.getAlphaColor(watermarkTheme
629: .getColorScheme().getDarkColor(), alpha);
630: }
631:
632: /**
633: * Returns a translucent of the specified color.
634: *
635: * @param color
636: * Color.
637: * @param alpha
638: * Alpha channel value.
639: * @return Translucent of the specified color that matches the requested
640: * alpha channel value.
641: */
642: public static Color getAlphaColor(Color color, int alpha) {
643: return new Color(color.getRed(), color.getGreen(), color
644: .getBlue(), alpha);
645: }
646:
647: /**
648: * Returns saturated version of the specified color.
649: *
650: * @param color
651: * Color.
652: * @param factor
653: * Saturation factor.
654: * @return Saturated color.
655: */
656: public static Color getSaturatedColor(Color color, double factor) {
657: float[] hsbvals = new float[3];
658: Color.RGBtoHSB(color.getRed(), color.getGreen(), color
659: .getBlue(), hsbvals);
660: float sat = hsbvals[1];
661: if (factor > 0.0) {
662: sat = sat + (float) factor * (1.0f - sat);
663: } else {
664: sat = sat + (float) factor * sat;
665: }
666: return new Color(Color.HSBtoRGB(hsbvals[0], sat, hsbvals[2]));
667: }
668:
669: /**
670: * Returns hue-shifted (in HSV space) version of the specified color.
671: *
672: * @param color
673: * Color.
674: * @param hueShift
675: * hue shift factor.
676: * @return Hue-shifted (in HSV space) color.
677: */
678: public static Color getHueShiftedColor(Color color, double hueShift) {
679: float[] hsbvals = new float[3];
680: Color.RGBtoHSB(color.getRed(), color.getGreen(), color
681: .getBlue(), hsbvals);
682: float hue = hsbvals[0];
683: hue += hueShift;
684: if (hue < 0.0)
685: hue += 1.0;
686: if (hue > 1.0)
687: hue -= 1.0;
688: return new Color(Color.HSBtoRGB(hue, hsbvals[1], hsbvals[2]));
689: }
690:
691: /**
692: * Derives a color based on the original color and a brightness source. The
693: * resulting color has the same hue and saturation as the original color,
694: * but its brightness is shifted towards the brightness of the brightness
695: * source. Thus, a light red color shifted towards dark green will become
696: * dark red.
697: *
698: * @param original
699: * Original color.
700: * @param brightnessSource
701: * Brightness source.
702: * @return Derived color that has the same hue and saturation as the
703: * original color, but its brightness is shifted towards the
704: * brightness of the brightness source.
705: */
706: public static Color deriveByBrightness(Color original,
707: Color brightnessSource) {
708: float[] hsbvalsOrig = new float[3];
709: Color.RGBtoHSB(original.getRed(), original.getGreen(), original
710: .getBlue(), hsbvalsOrig);
711: float[] hsbvalsBrightnessSrc = new float[3];
712: Color.RGBtoHSB(brightnessSource.getRed(), brightnessSource
713: .getGreen(), brightnessSource.getBlue(),
714: hsbvalsBrightnessSrc);
715: return new Color(Color.HSBtoRGB(hsbvalsOrig[0], hsbvalsOrig[1],
716: (hsbvalsBrightnessSrc[2] + hsbvalsOrig[2]) / 2.0f));
717:
718: }
719:
720: /**
721: * Returns the foreground color of the specified theme.
722: *
723: * @param theme
724: * Theme.
725: * @return Theme foreground color.
726: */
727: public static Color getForegroundColor(SubstanceTheme theme) {
728: return theme.getColorScheme().getForegroundColor();
729: }
730:
731: /**
732: * Returns lighter version of the specified color.
733: *
734: * @param color
735: * Color.
736: * @param diff
737: * Difference factor (values closer to 0.0 will produce results
738: * closer to white color).
739: * @return Lighter version of the specified color.
740: */
741: public static Color getLighterColor(Color color, double diff) {
742: int r = color.getRed() + (int) (diff * (255 - color.getRed()));
743: int g = color.getGreen()
744: + (int) (diff * (255 - color.getGreen()));
745: int b = color.getBlue()
746: + (int) (diff * (255 - color.getBlue()));
747: return new Color(r, g, b);
748: }
749:
750: /**
751: * Returns darker version of the specified color.
752: *
753: * @param color
754: * Color.
755: * @param diff
756: * Difference factor (values closer to 1.0 will produce results
757: * closer to black color).
758: * @return Darker version of the specified color.
759: */
760: public static Color getDarkerColor(Color color, double diff) {
761: int r = (int) ((1.0 - diff) * color.getRed());
762: int g = (int) ((1.0 - diff) * color.getGreen());
763: int b = (int) ((1.0 - diff) * color.getBlue());
764: return new Color(r, g, b);
765: }
766:
767: /**
768: * Converts the specified color into color-blind version.
769: *
770: * @param orig
771: * The original color.
772: * @param rgbToLms
773: * RGB to LMS conversion matrix.
774: * @param kind
775: * Color-blindness kind.
776: * @param lmsToRgb
777: * LMS to RGB conversion matrix.
778: * @return Color-blind version of the original color.
779: */
780: public static Color getColorBlindColor(Color orig,
781: double[][] rgbToLms, BlindnessKind kind, double[][] lmsToRgb) {
782: double r = orig.getRed();
783: double g = orig.getGreen();
784: double b = orig.getBlue();
785:
786: double[] rgbOrig = new double[] { r, g, b };
787: double[] lms = mult3(rgbToLms, rgbOrig);
788: double tmp = 0.0;
789:
790: double[] anchor = { 0.08008, 0.1579, 0.5897, 0.1284, 0.2237,
791: 0.3636, 0.9856, 0.7325, 0.001079, 0.0914, 0.007009, 0.0 };
792:
793: double[] rgbAnchor = {
794: rgbToLms[0][0] + rgbToLms[0][1] + rgbToLms[0][2],
795: rgbToLms[1][0] + rgbToLms[1][1] + rgbToLms[1][2],
796: rgbToLms[2][0] + rgbToLms[2][1] + rgbToLms[2][2] };
797:
798: double a1, a2, b1, b2, c1, c2, inflection;
799:
800: switch (kind) {
801: case PROTANOPIA:
802: a1 = rgbAnchor[1] * anchor[8] - rgbAnchor[2] * anchor[7];
803: b1 = rgbAnchor[2] * anchor[6] - rgbAnchor[0] * anchor[8];
804: c1 = rgbAnchor[0] * anchor[7] - rgbAnchor[1] * anchor[6];
805: a2 = rgbAnchor[1] * anchor[2] - rgbAnchor[2] * anchor[1];
806: b2 = rgbAnchor[2] * anchor[0] - rgbAnchor[0] * anchor[2];
807: c2 = rgbAnchor[0] * anchor[1] - rgbAnchor[1] * anchor[0];
808: inflection = rgbAnchor[2] / rgbAnchor[1];
809: tmp = lms[2] / lms[1];
810: if (tmp < inflection)
811: lms[0] = -(b1 * lms[1] + c1 * lms[2]) / a1;
812: else
813: lms[0] = -(b2 * lms[1] + c2 * lms[2]) / a2;
814: break;
815:
816: case DEUTERANOPIA:
817: a1 = rgbAnchor[1] * anchor[8] - rgbAnchor[2] * anchor[7];
818: b1 = rgbAnchor[2] * anchor[6] - rgbAnchor[0] * anchor[8];
819: c1 = rgbAnchor[0] * anchor[7] - rgbAnchor[1] * anchor[6];
820: a2 = rgbAnchor[1] * anchor[2] - rgbAnchor[2] * anchor[1];
821: b2 = rgbAnchor[2] * anchor[0] - rgbAnchor[0] * anchor[2];
822: c2 = rgbAnchor[0] * anchor[1] - rgbAnchor[1] * anchor[0];
823: inflection = rgbAnchor[2] / rgbAnchor[0];
824: tmp = lms[2] / lms[0];
825: /* See which side of the inflection line we fall... */
826: if (tmp < inflection)
827: lms[1] = -(a1 * lms[0] + c1 * lms[2]) / b1;
828: else
829: lms[1] = -(a2 * lms[0] + c2 * lms[2]) / b2;
830: break;
831:
832: case TRITANOPIA:
833: a1 = rgbAnchor[1] * anchor[11] - rgbAnchor[2] * anchor[10];
834: b1 = rgbAnchor[2] * anchor[9] - rgbAnchor[0] * anchor[11];
835: c1 = rgbAnchor[0] * anchor[10] - rgbAnchor[1] * anchor[9];
836: a2 = rgbAnchor[1] * anchor[5] - rgbAnchor[2] * anchor[4];
837: b2 = rgbAnchor[2] * anchor[3] - rgbAnchor[0] * anchor[5];
838: c2 = rgbAnchor[0] * anchor[4] - rgbAnchor[1] * anchor[3];
839: inflection = (rgbAnchor[1] / rgbAnchor[0]);
840: tmp = lms[1] / lms[0];
841: if (tmp < inflection)
842: lms[2] = -(a1 * lms[0] + b1 * lms[1]) / c1;
843: else
844: lms[2] = -(a2 * lms[0] + b2 * lms[1]) / c2;
845: break;
846:
847: default:
848: break;
849: }
850: double[] rgbCb = mult3(lmsToRgb, lms);
851:
852: double nr = Math.min(255.0, Math.max(0.0, rgbCb[0]));
853: double ng = Math.min(255.0, Math.max(0.0, rgbCb[1]));
854: double nb = Math.min(255.0, Math.max(0.0, rgbCb[2]));
855: return new Color((int) nr, (int) ng, (int) nb);
856: }
857:
858: /**
859: * Multiplies the specified 3x3 matrix by the specified 3x1 vector.
860: *
861: * @param matrix
862: * Matrix.
863: * @param vector
864: * Vector.
865: * @return Vector multiplication.
866: */
867: private static double[] mult3(double[][] matrix, double[] vector) {
868: double[] res = new double[3];
869: res[0] = matrix[0][0] * vector[0] + matrix[0][1] * vector[1]
870: + matrix[0][2] * vector[2];
871: res[1] = matrix[1][0] * vector[0] + matrix[1][1] * vector[1]
872: + matrix[1][2] * vector[2];
873: res[2] = matrix[2][0] * vector[0] + matrix[2][1] * vector[1]
874: + matrix[2][2] * vector[2];
875: return res;
876: }
877:
878: /**
879: * Returns the brightness of the specified color.
880: *
881: * @param rgb
882: * RGB value of a color.
883: * @return The brightness of the specified color.
884: */
885: public static int getColorBrightness(int rgb) {
886: int oldR = (rgb >>> 16) & 0xFF;
887: int oldG = (rgb >>> 8) & 0xFF;
888: int oldB = (rgb >>> 0) & 0xFF;
889:
890: return (222 * oldR + 707 * oldG + 71 * oldB) / 1000;
891: }
892:
893: /**
894: * Returns the color of the focus ring for the specified component.
895: *
896: * @param comp
897: * Component.
898: * @return The color of the focus ring for the specified component.
899: */
900: public static Color getFocusColor(Component comp) {
901: SubstanceTheme activeTheme = SubstanceThemeUtilities.getTheme(
902: comp, ComponentState.ACTIVE);
903:
904: if (comp instanceof AbstractButton) {
905: AbstractButton ab = (AbstractButton) comp;
906: ButtonModel model = ab.getModel();
907:
908: ComponentState currState = ComponentState.getState(model,
909: ab);
910: SubstanceTheme currTheme = SubstanceThemeUtilities
911: .getTheme(comp, currState);
912: Color currColor = SubstanceCoreUtilities
913: .isThemeDark(currTheme) ? currTheme
914: .getColorScheme().getUltraDarkColor() : currTheme
915: .getColorScheme().getDarkColor();
916:
917: FadeState fadeState = SubstanceFadeUtilities.getFadeState(
918: comp, FadeKind.PRESS, FadeKind.SELECTION,
919: FadeKind.ROLLOVER);
920: if (fadeState != null) {
921: // the component is currently animating
922: ComponentState prevState = SubstanceCoreUtilities
923: .getPrevComponentState(ab);
924:
925: SubstanceTheme prevTheme = SubstanceThemeUtilities
926: .getTheme(comp, prevState);
927:
928: Color prevColor = SubstanceCoreUtilities
929: .isThemeDark(prevTheme) ? prevTheme
930: .getColorScheme().getUltraDarkColor()
931: : prevTheme.getColorScheme().getDarkColor();
932:
933: float likeness = fadeState.getFadePosition() / 10.0f;
934: if (fadeState.isFadingIn())
935: likeness = 1.0f - likeness;
936: return SubstanceColorUtilities.getInterpolatedColor(
937: prevColor, currColor, likeness);
938: } else {
939: return currColor;
940: }
941: }
942:
943: Color color = SubstanceCoreUtilities.isThemeDark(activeTheme) ? activeTheme
944: .getColorScheme().getUltraDarkColor()
945: : activeTheme.getColorScheme().getDarkColor();
946: return color;
947: }
948:
949: /**
950: * Returns the color strength.
951: *
952: * @param color
953: * Color.
954: * @return Color strength.
955: */
956: public static float getColorStrength(Color color) {
957: return Math.max(getColorBrightness(color.getRGB()),
958: getColorBrightness(getNegativeColor(color.getRGB()))) / 255.0f;
959: }
960: }
|