1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.utils.ui.color;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.graphics.Color;
21 import org.eclipse.swt.graphics.GC;
22 import org.eclipse.swt.graphics.Image;
23 import org.eclipse.swt.widgets.Display;
28 * Color gradient between multiple colors
31 * Supports RGB and HSV linear interpolation between colors
34 * @author Marko Luukkainen
37 public class ColorGradient {
38 public static final int RGB = 0;
40 public static final int HSV = 1;
42 protected ArrayList<ColorValue> values;
46 public ColorGradient() {
47 this.values = new ArrayList<ColorValue>();
51 public ColorGradient(ColorGradient copyFrom) {
52 this.values = new ArrayList<ColorValue>(copyFrom.values);
53 this.type = copyFrom.type;
56 public ColorGradient(Collection<ColorValue> values) {
57 this.values = new ArrayList<ColorValue>(values);
58 Collections.sort(this.values, new ColorValueComparator());
62 public ColorGradient(ColorValue array[]) {
63 this.values = new ArrayList<ColorValue>(array.length);
64 for (ColorValue c : array) {
67 Collections.sort(this.values, new ColorValueComparator());
71 public ColorGradient(Collection<ColorValue> values, int type) {
72 this.values = new ArrayList<ColorValue>(values);
73 Collections.sort(this.values, new ColorValueComparator());
77 public ColorGradient(ColorValue array[], int type) {
78 this.values = new ArrayList<ColorValue>();
79 for (ColorValue c : array) {
82 Collections.sort(this.values, new ColorValueComparator());
87 * Interpolates color in RGB space
92 private byte[] getRGBColor(double value) {
94 while (values.get(index).getValue() <= value && index < values.size()-1)
97 value -= values.get(index - 1).getValue();
98 value /= (values.get(index).getValue() - values.get(index - 1).getValue());
99 double valuei = 1.0 - value;
100 byte color[] = new byte[] {
101 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getR() + valuei * values.get(index - 1).getColor().getR())),
102 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getG() + valuei * values.get(index - 1).getColor().getG())),
103 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getB() + valuei * values.get(index - 1).getColor().getB())) };
109 * Interpolates color in HSV space
114 private byte[] getHSVColor(double value) {
116 while (values.get(index).getValue() <= value && index < values.size()-1)
119 value -= values.get(index - 1).getValue();
120 value /= (values.get(index).getValue() - values.get(index - 1).getValue());
121 double valuei = 1.0 - value;
123 if (Float.isNaN(values.get(index).getColor().getH())) {
124 h = values.get(index-1).getColor().getH();
125 } else if (Float.isNaN(values.get(index-1).getColor().getH())) {
126 h = values.get(index).getColor().getH();
128 // selecting shortest direction between hues
129 float angle = values.get(index).getColor().getH() - values.get(index - 1).getColor().getH();
132 else if (angle < -180.f)
134 h = values.get(index - 1).getColor().getH() + value * angle;
140 org.simantics.utils.ui.color.Color interpolated = new org.simantics.utils.ui.color.Color(h, value * values.get(index).getColor().getS() + valuei * values.get(index - 1).getColor().getS(),
141 value * values.get(index).getColor().getV() + valuei * values.get(index - 1).getColor().getV());
142 byte color[] = new byte[] { (byte) interpolated.getR(), (byte) interpolated.getG(), (byte) interpolated.getB() };
150 * Returns gradient in array of bytes. Array is RGB order and int contains 3 * requested size of bytes.
153 * If gradient contains only one color array is filled with that color
156 * if gradient has no colors array is filled with white
158 * @param size number of pixels
159 * @return gradient in array of bytes
161 public byte[] getGradientArray(int size) {
162 byte array[] = new byte[size * 3];
163 if (values.size() > 1) {
164 for (int i = 0; i < size; i++) {
166 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) i / (double) size;
169 color = getRGBColor(value);
171 color = getHSVColor(value);
172 array[index] = color[0];
173 array[index + 1] = color[1];
174 array[index + 2] = color[2];
176 } else if (values.size() == 1) {
177 byte color[] = new byte[3];
178 color[0] = (byte)values.get(0).getColor().getR();
179 color[1] = (byte)values.get(0).getColor().getG();
180 color[2] = (byte)values.get(0).getColor().getB();
181 for (int i = 0; i < size; i++) {
183 array[index] = color[0];
184 array[index + 1] = color[1];
185 array[index + 2] = color[2];
188 for (int i = 0; i < size; i++) {
190 array[index] = (byte)255;
191 array[index + 1] = (byte)255;
192 array[index + 2] = (byte)255;
200 * Returns gradient in image.
203 * If gradient contains only one color image is filled with that color
206 * if gradient has no colors image is filled with white
209 * Style must be set to <code>SWT.HORIZONTAL</code> or <code>SWT.VERTICAL</code>
211 * @param size number of pixels
212 * @return gradient in array of bytes
215 public Image getGradientImage(int width, int height, int style) {
216 Image image = new Image(Display.getCurrent(), width, height);
217 GC gc = new GC(image);
218 gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
219 gc.fillRectangle(0, 0, width, height);
220 if (values.size() > 1) {
221 if (SWT.HORIZONTAL == (style | SWT.HORIZONTAL)) {
222 for (int x = 0; x < width; x++) {
223 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) x
224 / (double) (width - 1);
227 byteColor = getRGBColor(value);
229 byteColor = getHSVColor(value);
230 Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);
231 gc.setForeground(color);
232 gc.drawLine(x, 0, x, height);
235 } else if (SWT.VERTICAL == (style | SWT.VERTICAL)){
236 for (int y = 0; y < height; y++) {
237 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) y
238 / (double) (height - 1);
241 byteColor = getRGBColor(value);
243 byteColor = getHSVColor(value);
244 Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);
245 gc.setForeground(color);
246 gc.drawLine(0, y, width, y);
252 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
254 } else if (values.size() == 1) {
255 Color color = new Color(Display.getCurrent(), values.get(0).getColor().getR(), values.get(0).getColor().getG(), values.get(0).getColor().getB());
256 gc.setBackground(color);
257 gc.fillRectangle(0, 0, width, height);
260 gc.fillRectangle(0, 0, width, height);
266 public int getType() {
270 public List<ColorValue> getColorValues() {
274 public ColorValue[] getColorValueArray() {
275 return values.toArray(new ColorValue[values.size()]);
279 public void addCastorColorValue(ColorValue value) {
281 Collections.sort(this.values, new ColorValueComparator());
285 public void setCastorType(int type) {
290 public int hashCode() {
292 for (ColorValue cv : values)
293 hash ^= cv.hashCode();
298 public boolean equals(Object obj) {
301 if (obj.getClass() != getClass())
303 ColorGradient cg = (ColorGradient) obj;
304 if (cg.type != type) return false;
305 if (values.size()!=cg.values.size()) return false;
306 if (!values.containsAll(cg.values)) return false;