|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ /* * RelativeColor.java * * Created on March 13, 2004, 1:37 PM */ package org.netbeans.swing.plaf.util; import javax.swing.*; import java.awt.*; /** A color which can be placed into UIDefaults, which is computed from: *
What this class is for* A number of components in NetBeans have colors that should be based on a * color taken from the desktop theme. Swing provides a mechanism for getting * these colors, via UIManager, which will supply correct colors based on the * desktop theme for a variety of operating systems. ** But often the color in a UI specification is not the same as, but related to * the color that should be used. For example, in windows classic, the tabs * have a gradient based on a light blue color. The color should be related to * the dark blue color normally used in Windows for window titles. However, * if the user has set the window titlebar color to red, a reddish color should * be used. *
* This class allows you to provide a base value (the default Windows
* titlebar color, hardcoded) and a prototype value (the blue color that should
* be used if the desktop colors are the defaults), and the actual
* value retrieved from the UI. The instance of this class is then dropped
* into How it does what it does* The base and prototype are split into HSB color components. The relationship * between the base and prototype values in saturation and brightness is then * computed. This same relationship is then applied to the actual value * as a function of the divergence between the base and actual values * such that the more a color diverges, the less the relationship is applied - * so that, if the base color is dark blue and the prototype color is light * blue, but the actual color is light yellow, you get light yellow (as opposed * to pure white, which a naive application of the relationship would get). * *Note: It is possible to create cyclic * references between RelativeColor instances (for example, a RelativeColor * that has its own key as one of the keys it should fetch). Don't do that. * * @author Tim Boudreau */ public class RelativeColor implements UIDefaults.LazyValue { private Color value = null; private Color fallback = null; /** Creates a new instance of RelativeColor. * * @param base A Color or UIManager key for a color that the target color is related to * @param target A Color or UIManager key for a color that is what the target color should be if the * actual color is equal to the base color * @param actual Either a Color object or a UIManager String key resolvable * to a color, which represents the * actual color, which may or may not match the target color * @param mustContrast Either a Color object or a UIManager String key * resolvable to a color which must contrast sufficiently with the derived * color that text will be readable. This parameter may be null; the others * may not. */ public RelativeColor(Object base, Object target, Object actual, Object mustContrast) { if (base == null || target == null || actual == null) { throw new NullPointerException ("Null argument(s): " + base + ',' + target + ',' + actual + ',' + mustContrast); } if (base instanceof String) { baseColorKey = (String) base; } else { baseColor = (Color) base; } if (target instanceof String) { targetColorKey = (String) target; } else { targetColor = (Color) target; } if (actual instanceof String) { actualColorKey = (String) actual; } else { actualColor = (Color) actual; } if (mustContrast != null) { if (mustContrast instanceof String) { mustContrastColorKey = (String) mustContrast; } else { mustContrastColor = (Color) mustContrast; } } } /** Creates a new instance of RelativeColor. * * @param base A Color that the target color is related to * @param target A Color that is what the target color should be if the * actual color is equal to the base color * @param actual Either a Color object or a UIManager String key resolvable * to a color, which represents the * actual color, which may or may not match the target color * @param mustContrast Either a Color object or a UIManager String key * resolvable to a color which must contrast sufficiently with the derived * color that text will be readable */ public RelativeColor(Color base, Color target, Object actual) { this (base, target, actual, null); } public void clear() { value = null; if (actualColorKey != null) { actualColor = null; } if (targetColorKey != null) { targetColor = null; } if (mustContrastColorKey != null) { mustContrastColor = null; } if (baseColorKey != null) { baseColor = null; } } public Object createValue(UIDefaults table) { if (value != null) { return value; } Color actual = getActualColor(); Color base = getBaseColor(); if (actual.equals(base)) { value = getTargetColor(); } else { value = deriveColor (base, actual, getTargetColor()); } if (hasMustContrastColor()) { value = ensureContrast(value, getMustContrastColor()); } return value; } /** Convenience getter, as this class is reasonably useful for creating * derived colors without putting them into UIDefaults */ public Color getColor() { return (Color) createValue(null); } private Color targetColor = null; private String targetColorKey = null; private Color getTargetColor() { if (checkState (targetColor, targetColorKey)) { targetColor = fetchColor(targetColorKey); } return targetColor; } private Color baseColor = null; private String baseColorKey = null; private Color getBaseColor() { if (checkState (baseColor, baseColorKey)) { baseColor = fetchColor(baseColorKey); } return baseColor; } private Color mustContrastColor = null; private String mustContrastColorKey = null; private Color getMustContrastColor() { if (checkState (mustContrastColor, mustContrastColorKey)) { mustContrastColor = fetchColor(mustContrastColorKey); } return mustContrastColor; } private Color actualColor = null; private String actualColorKey = null; private Color getActualColor() { if (checkState (actualColor, actualColorKey)) { actualColor = fetchColor(actualColorKey); } return actualColor; } private boolean hasMustContrastColor() { return mustContrastColor != null || mustContrastColorKey != null; } /** Ensures that the key and color are not null, and returns true if the * color needs to be loaded. */ private boolean checkState(Color color, String key) { if (color == null && key == null) { throw new NullPointerException("Both color and key are null for " + this); } return color == null; } private Color fetchColor(String key) { //Todo - check for cyclic references Color result = UIManager.getColor(key); if (result == null) { result = fallback; } return result; } /** Does the actual leg-work of deriving the color */ static Color deriveColor (Color base, Color actual, Color target) { float[] baseHSB = Color.RGBtoHSB(base.getRed(), base.getGreen(), base.getBlue(), null); float[] targHSB = Color.RGBtoHSB(target.getRed(), target.getGreen(), target.getBlue(), null); float[] actualHSB = Color.RGBtoHSB(actual.getRed(), actual.getGreen(), actual.getBlue(), null); float[] resultHSB = new float[3]; float[] finalHSB = new float[3]; float[] diff = percentageDiff (actualHSB, baseHSB); resultHSB[0] = actualHSB[0] + (diff[0] * (targHSB[0] - baseHSB[0])); resultHSB[1] = actualHSB[1] + (diff[1] * (targHSB[1] - baseHSB[1])); resultHSB[2] = actualHSB[2] + (diff[2] * (targHSB[2] - baseHSB[2])); finalHSB[0] = saturate (resultHSB[0]); finalHSB[1] = saturate (resultHSB[1]); finalHSB[2] = saturate (resultHSB[2]); //If the target had *some* color, so should our result - if it pretty //much doesn't, redistribute some of the brightness to the saturation value if (targHSB[1] > 0.1 && resultHSB[1] <= 0.1) { resultHSB[1] = resultHSB[2] * 0.25f; resultHSB[2] = resultHSB[2] - (resultHSB[2] * 0.25f); } Color result = new Color (Color.HSBtoRGB(finalHSB[0], finalHSB[1], finalHSB[2])); return result; } private static float[] percentageDiff (float[] a, float[] b) { float[] result = new float[3]; for (int i=0; i < 3; i++) { result[i] = 1 - Math.abs(a[i] - b[i]); if (result[i] == 0) { result[i] = 1- a[i]; } } return result; } private static final void out (String nm, float[] f) { //XXX for debugging - deleteme StringBuffer sb = new StringBuffer(nm); sb.append(": "); for (int i=0; i < f.length; i++) { sb.append (Math.round(f[i] * 100)); if (i != f.length-1) { sb.append(','); sb.append(' '); } } System.err.println(sb.toString()); } /** Saturate a float value, clamping values below 0 to 0 and above 1 to 1 */ private static float saturate (float f) { return Math.max(0, Math.min(1, f)); } static Color ensureContrast (Color target, Color contrast) { //XXX - this needs some work. What it should really do: //Determine the distance from 0.5 for brightness and saturation of the contrasting color, to //determine the direction in which to adjust. Then adjust in that //direction as a function of the diff between 0.25 and 0.5 of the //diff between the colors...or something like that. The point is //there's a danger zone around 0.5 where things that should be //adjusted away from each other aren't being float[] contHSB = Color.RGBtoHSB(contrast.getRed(), contrast.getGreen(), contrast.getBlue(), null); float[] targHSB = Color.RGBtoHSB(target.getRed(), target.getGreen(), target.getBlue(), null); float[] resultHSB = new float[3]; System.arraycopy(targHSB, 0, resultHSB, 0, 3); float satDiff = Math.abs (targHSB[1] - contHSB[1]); float briDiff = Math.abs (targHSB[2] - contHSB[2]); if (targHSB[1] > 0.6 && resultHSB[1] > 0.6 || (briDiff < 0.45f && satDiff < 0.4f)) { resultHSB[1] /= 3; // System.err.println("adjusting saturation to " + resultHSB[1] + " from " + targHSB[1]); satDiff = Math.abs (targHSB[1] - contHSB[1]); } if (briDiff < 0.3 || (satDiff < 0.3 && briDiff < 0.5)) { float dir = 1.5f * (0.5f - contHSB[2]); resultHSB[2] = saturate (resultHSB[2] + dir); // System.err.println("adjusting brightness to " + resultHSB[2] + " from " + targHSB[2]); } Color result = new Color (Color.HSBtoRGB(resultHSB[0], resultHSB[1], resultHSB[2])); return result; } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.