001: /*******************************************************************************
002: * Copyright (c) 2004, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.themes;
011:
012: import java.util.Hashtable;
013:
014: import org.eclipse.core.runtime.CoreException;
015: import org.eclipse.core.runtime.IConfigurationElement;
016: import org.eclipse.core.runtime.IExecutableExtension;
017: import org.eclipse.swt.graphics.RGB;
018: import org.eclipse.ui.themes.ColorUtil;
019: import org.eclipse.ui.themes.IColorFactory;
020:
021: /**
022: * A <code>IColorFactory</code> that may be used to select a colour with
023: * a higher contrast. The colors to blend are specified as per method
024: * number two in {@link org.eclipse.core.runtime.IExecutableExtension}.
025: * <p>
026: * Example usage:
027: * <br/>
028: * <code>
029: * <colorDefinition
030: * label="Red/Blue Contrast"
031: * id="example.redblueblend">
032: * <colorFactory
033: * plugin="org.eclipse.ui"
034: * class="org.eclipse.ui.internal.themes.RGBContrastFactory">
035: * <parameter name="foreground" value="0,0,0" />
036: * <parameter name="background1" value="COLOR_RED" />
037: * <parameter name="background2" value="COLOR_BLUE" />
038: * </colorFactory>
039: * </colorDefinition>
040: * </code>
041: * </p>
042: *
043: * <p>
044: * This will select whichever of Red or Blue has a higher contrst with black.
045: * The color values may be specified as RGB triples or as SWT constants.
046: * </p>
047: *
048: * @see org.eclipse.swt.SWT
049: * @since 3.0
050: */
051: public class RGBContrastFactory implements IColorFactory,
052: IExecutableExtension {
053: private String fg, bg1, bg2;
054:
055: /**
056: * Returns the intensity of an RGB component using the
057: * sRGB gamma function.
058: *
059: * @param val Value to convert.
060: * @return Light intensity of the component.
061: */
062: double voltage_to_intensity_srgb(double val) {
063: /* Handle invalid values before doing a gamma transform. */
064: if (val < 0.0) {
065: return 0.0;
066: }
067: if (val > 1.0) {
068: return 1.0;
069: }
070:
071: if (val <= 0.04045) {
072: return val / 12.92;
073: }
074: return Math.pow((val + 0.055) / 1.055, 2.4);
075: }
076:
077: /**
078: * Returns a measure of the lightness in the perceptual colourspace
079: * IPT.
080: *
081: * @param color The colour in sRGB
082: * @return Lightness in IPT space.
083: */
084: double lightness(RGB color) {
085: double r = voltage_to_intensity_srgb(color.red / 255.0);
086: double g = voltage_to_intensity_srgb(color.green / 255.0);
087: double b = voltage_to_intensity_srgb(color.blue / 255.0);
088: double l = (0.3139 * r) + (0.6395 * g) + (0.0466 * b);
089: double m = (0.1516 * r) + (0.7482 * g) + (0.1000 * b);
090: double s = (0.0177 * r) + (0.1095 * g) + (0.8729 * b);
091: double lp, mp, sp;
092:
093: if (l < 0.0) {
094: lp = -Math.pow(-l, 0.43);
095: } else {
096: lp = Math.pow(l, 0.43);
097: }
098: if (m < 0.0) {
099: mp = -Math.pow(-m, 0.43);
100: } else {
101: mp = Math.pow(m, 0.43);
102: }
103: if (s < 0.0) {
104: sp = -Math.pow(-s, 0.43);
105: } else {
106: sp = Math.pow(s, 0.43);
107: }
108:
109: return (0.4000 * lp) + (0.4000 * mp) + (0.2000 * sp);
110: }
111:
112: public RGB createColor() {
113: /**
114: * Determine which pair has a higher contrast by selecting
115: * the colour with the furthest distance in lightness.
116: */
117: RGB cfg, cbg1, cbg2;
118:
119: if (fg != null) {
120: cfg = ColorUtil.getColorValue(fg);
121: } else {
122: cfg = new RGB(255, 255, 255);
123: }
124: if (bg1 != null) {
125: cbg1 = ColorUtil.getColorValue(bg1);
126: } else {
127: cbg1 = new RGB(0, 0, 0);
128: }
129: if (bg2 != null) {
130: cbg2 = ColorUtil.getColorValue(bg2);
131: } else {
132: cbg2 = new RGB(0, 0, 0);
133: }
134:
135: double lfg = lightness(cfg);
136: double lbg1 = lightness(cbg1);
137: double lbg2 = lightness(cbg2);
138:
139: if (Math.abs(lbg1 - lfg) > Math.abs(lbg2 - lfg)) {
140: return cbg1;
141: } else {
142: return cbg2;
143: }
144: }
145:
146: /**
147: * This executable extension requires parameters to be explicitly declared
148: * via the second method described in the <code>IExecutableExtension</code>
149: * documentation. This class expects that there will be three parameters,
150: * <code>foreground</code>, <code>background1</code> and
151: * <code>background2</code>, that describe the two colors to be blended.
152: * These values may either be RGB triples or SWT constants.
153: *
154: * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
155: */
156: public void setInitializationData(IConfigurationElement config,
157: String propertyName, Object data) throws CoreException {
158: if (data instanceof Hashtable) {
159: Hashtable table = (Hashtable) data;
160: fg = (String) table.get("foreground"); //$NON-NLS-1$
161: bg1 = (String) table.get("background1"); //$NON-NLS-1$
162: bg2 = (String) table.get("background2"); //$NON-NLS-1$
163: }
164:
165: }
166: }
|