Mercurial > projects > dwt-addons
diff dwtx/jface/internal/text/revisions/Colors.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/internal/text/revisions/Colors.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.internal.text.revisions.Colors; + +import dwt.dwthelper.utils; + + +import dwt.DWT; +import dwt.graphics.RGB; +import dwtx.core.runtime.Assert; + +/** + * Utility for color operations. + * + * @since 3.3 + */ +public final class Colors { + /* + * Implementation note: Color computation assumes sRGB, which is probably not true, and does not + * always give good results. CIE based algorithms would be better, see + * http://www.w3.org/TR/PNG-ColorAppendix.html and http://en.wikipedia.org/wiki/Lab_color_space + */ + + /** + * Returns the human-perceived brightness of a color as float in [0.0, 1.0]. The used RGB + * weights come from http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC9. + * + * @param rgb the color + * @return the gray-scale value + */ + public static float brightness(RGB rgb) { + return Math.min(1f, (0.2126f * rgb.red + 0.7152f * rgb.green + 0.0722f * rgb.blue + 0.5f) / 255f); + } + + /** + * Normalizes a color in its perceived brightness. Yellows are darkened, while blues and reds + * are lightened. Depending on the hue, the brightness range within the RGB gamut may be + * different, outside values are clipped. Note that this is an approximation; the returned RGB + * is not guaranteed to have the requested {@link #brightness(RGB) brightness}. + * + * @param color the color to normalize + * @param brightness the requested brightness, in [0, 1] + * @return a normalized version of <code>color</code> + * @see #brightness(RGB) + */ + public static RGB adjustBrightness(RGB color, float brightness) { + float[] hsi= toHSI(color); + float psychoFactor= brightness - brightness(color); + float weight= 0.5f; // found by trial and error + hsi[2]= Math.max(0, Math.min(1.0f, hsi[2] + psychoFactor * weight)); + color= fromHSI(hsi); + return color; + } + + /** + * Converts an {@link RGB} to an <a href="http://en.wikipedia.org/wiki/HSL_color_space">HSI</a> + * triplet. + * + * @param color the color to convert + * @return the HSI float array of length 3 + */ + private static float[] toHSI(RGB color) { + float r = color.red / 255f; + float g = color.green / 255f; + float b = color.blue / 255f; + float max = Math.max(Math.max(r, g), b); + float min = Math.min(Math.min(r, g), b); + float delta = max - min; + float maxPlusMin= max + min; + float intensity = maxPlusMin / 2; + float saturation= intensity < 0.5 ? delta / maxPlusMin : delta / (2 - maxPlusMin); + + float hue = 0; + if (delta !is 0) { + if (r is max) { + hue = (g - b) / delta; + } else { + if (g is max) { + hue = 2 + (b - r) / delta; + } else { + hue = 4 + (r - g) / delta; + } + } + hue *= 60; + if (hue < 0) hue += 360; + } + return new float[] {hue, saturation, intensity}; + } + + /** + * Converts a <a href="http://en.wikipedia.org/wiki/HSL_color_space">HSI</a> triplet to an RGB. + * + * @param hsi the HSI values + * @return the RGB corresponding to the HSI spec + */ + private static RGB fromHSI(float[] hsi) { + float r, g, b; + float hue= hsi[0]; + float saturation= hsi[1]; + float intensity= hsi[2]; + if (saturation is 0) { + r = g = b = intensity; + } else { + float temp2= intensity < 0.5f ? intensity * (1.0f + saturation) : (intensity + saturation) - (intensity * saturation); + float temp1= 2f * intensity - temp2; + if (hue is 360) hue = 0; + hue /= 360; + + r= hue2RGB(temp1, temp2, hue + 1f/3f); + g= hue2RGB(temp1, temp2, hue); + b= hue2RGB(temp1, temp2, hue - 1f/3f); + } + + int red = (int)(r * 255 + 0.5); + int green = (int)(g * 255 + 0.5); + int blue = (int)(b * 255 + 0.5); + return new RGB(red, green, blue); + } + + private static float hue2RGB(float t1, float t2, float hue) { + if (hue < 0) + hue += 1; + else if (hue > 1) + hue -= 1; + if (6f * hue < 1) + return t1 +(t2 - t1) * 6f * hue; + if (2f * hue < 1) + return t2; + if (3f * hue < 2) + return t1 + (t2 - t1) * (2f/3f - hue) * 6f; + return t1; + } + + /** + * Returns an RGB that lies between the given foreground and background + * colors using the given mixing factor. A <code>factor</code> of 1.0 will produce a + * color equal to <code>fg</code>, while a <code>factor</code> of 0.0 will produce one + * equal to <code>bg</code>. + * @param bg the background color + * @param fg the foreground color + * @param factor the mixing factor, must be in [0, 1] + * + * @return the interpolated color + */ + public static RGB blend(RGB bg, RGB fg, float factor) { + Assert.isLegal(bg !is null); + Assert.isLegal(fg !is null); + Assert.isLegal(factor >= 0f && factor <= 1f); + + float complement= 1f - factor; + return new RGB( + (int) (complement * bg.red + factor * fg.red), + (int) (complement * bg.green + factor * fg.green), + (int) (complement * bg.blue + factor * fg.blue) + ); + } + + /** + * Returns an array of colors in a smooth palette from <code>start</code> to <code>end</code>. + * <p> + * The returned array has size <code>steps</code>, and the color at index 0 is <code>start</code>, the color + * at index <code>steps - 1</code> is <code>end</code>. + * + * @param start the start color of the palette + * @param end the end color of the palette + * @param steps the requested size, must be > 0 + * @return an array of <code>steps</code> colors in the palette from <code>start</code> to <code>end</code> + */ + public static RGB[] palette(RGB start, RGB end, int steps) { + Assert.isLegal(start !is null); + Assert.isLegal(end !is null); + Assert.isLegal(steps > 0); + + if (steps is 1) + return new RGB[] { start }; + + float step= 1.0f / (steps - 1); + RGB[] gradient= new RGB[steps]; + for (int i= 0; i < steps; i++) + gradient[i]= blend(start, end, step * i); + + return gradient; + } + + /** + * Returns an array of colors with hues evenly distributed on the hue wheel defined by the <a + * href="http://en.wikipedia.org/wiki/HSV_color_space">HSB color space</a>. The returned array + * has size <code>steps</code>. The distance <var>d</var> between two successive colors is + * in [120°, 180°]. + * <p> + * The color at a given <code>index</code> has the hue returned by + * {@linkplain #computeHue(int) computeHue(index)}; i.e. the computed hues are not equidistant, + * but adaptively distributed on the color wheel. + * </p> + * <p> + * The first six colors returned correspond to the following {@link DWT} color constants: + * {@link DWT#COLOR_RED red}, {@link DWT#COLOR_GREEN green}, {@link DWT#COLOR_BLUE blue}, + * {@link DWT#COLOR_YELLOW yellow}, {@link DWT#COLOR_CYAN cyan}, + * {@link DWT#COLOR_MAGENTA magenta}. + * </p> + * + * @param steps the requested size, must be >= 2 + * @return an array of <code>steps</code> colors evenly distributed on the color wheel + */ + public static RGB[] rainbow(int steps) { + Assert.isLegal(steps >= 2); + + RGB[] rainbow= new RGB[steps]; + for (int i= 0; i < steps; i++) + rainbow[i]= new RGB(computeHue(i), 1f, 1f); + + return rainbow; + } + + /** + * Returns an indexed hue in [0°, 360°), distributing the hues evenly on the hue wheel + * defined by the <a href="http://en.wikipedia.org/wiki/HSV_color_space">HSB (or HSV) color + * space</a>. The distance <var>d</var> between two successive colors is in [120°, 180°]. + * <p> + * The first six colors returned correspond to the following {@link DWT} color constants: + * {@link DWT#COLOR_RED red}, {@link DWT#COLOR_GREEN green}, {@link DWT#COLOR_BLUE blue}, + * {@link DWT#COLOR_YELLOW yellow}, {@link DWT#COLOR_CYAN cyan}, + * {@link DWT#COLOR_MAGENTA magenta}. + * </p> + * + * @param index the index of the color, must be >= 0 + * @return a color hue in [0°, 360°) + * @see RGB#RGB(float, float, float) + */ + public static float computeHue(final int index) { + Assert.isLegal(index >= 0); + /* + * Base 3 gives a nice partitioning for RGB colors with red, green, blue being the colors + * 0,1,2, and yellow, cyan, magenta colors 3,4,5. + */ + final int base= 3; + final float range= 360f; + + // partition the baseRange by using the least significant bit to select one half of the + // partitioning + int baseIndex= index / base; + float baseRange= range / base; + float baseOffset= 0f; + while (baseIndex > 0) { + baseRange /= 2; + int lsb= baseIndex % 2; + baseOffset += lsb * baseRange; + baseIndex >>= 1; + } + + final int baseMod= index % base; + final float hue= baseOffset + baseMod * range / base; + Assert.isTrue(hue >= 0 && hue < 360); + return hue; + } + + private Colors() { + // not instantiatable + } + +}