1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.utils.ui.color;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Collection;
\r
16 import java.util.Collections;
\r
18 import org.eclipse.swt.SWT;
\r
19 import org.eclipse.swt.graphics.Color;
\r
20 import org.eclipse.swt.graphics.GC;
\r
21 import org.eclipse.swt.graphics.Image;
\r
22 import org.eclipse.swt.widgets.Display;
\r
27 * Color gradient between multiple colors
\r
30 * Supports RGB and HSV linear interpolation between colors
\r
33 * @author Marko Luukkainen
\r
36 public class ColorGradient {
\r
37 public static final int RGB = 0;
\r
39 public static final int HSV = 1;
\r
41 protected ArrayList<ColorValue> values;
\r
45 public ColorGradient() {
\r
46 this.values = new ArrayList<ColorValue>();
\r
50 public ColorGradient(ColorGradient copyFrom) {
\r
51 this.values = new ArrayList<ColorValue>(copyFrom.values);
\r
52 this.type = copyFrom.type;
\r
55 public ColorGradient(Collection<ColorValue> values) {
\r
56 this.values = new ArrayList<ColorValue>(values);
\r
57 Collections.sort(this.values, new ColorValueComparator());
\r
61 public ColorGradient(ColorValue array[]) {
\r
62 this.values = new ArrayList<ColorValue>(array.length);
\r
63 for (ColorValue c : array) {
\r
66 Collections.sort(this.values, new ColorValueComparator());
\r
70 public ColorGradient(Collection<ColorValue> values, int type) {
\r
71 this.values = new ArrayList<ColorValue>(values);
\r
72 Collections.sort(this.values, new ColorValueComparator());
\r
76 public ColorGradient(ColorValue array[], int type) {
\r
77 this.values = new ArrayList<ColorValue>();
\r
78 for (ColorValue c : array) {
\r
81 Collections.sort(this.values, new ColorValueComparator());
\r
86 * Interpolates color in RGB space
\r
91 private byte[] getRGBColor(double value) {
\r
93 while (values.get(index).getValue() <= value && index < values.size()-1)
\r
96 value -= values.get(index - 1).getValue();
\r
97 value /= (values.get(index).getValue() - values.get(index - 1).getValue());
\r
98 double valuei = 1.0 - value;
\r
99 byte color[] = new byte[] {
\r
100 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getR() + valuei * values.get(index - 1).getColor().getR())),
\r
101 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getG() + valuei * values.get(index - 1).getColor().getG())),
\r
102 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getB() + valuei * values.get(index - 1).getColor().getB())) };
\r
108 * Interpolates color in HSV space
\r
113 private byte[] getHSVColor(double value) {
\r
115 while (values.get(index).getValue() <= value && index < values.size()-1)
\r
118 value -= values.get(index - 1).getValue();
\r
119 value /= (values.get(index).getValue() - values.get(index - 1).getValue());
\r
120 double valuei = 1.0 - value;
\r
122 if (Float.isNaN(values.get(index).getColor().getH())) {
\r
123 h = values.get(index-1).getColor().getH();
\r
124 } else if (Float.isNaN(values.get(index-1).getColor().getH())) {
\r
125 h = values.get(index).getColor().getH();
\r
127 // selecting shortest direction between hues
\r
128 float angle = values.get(index).getColor().getH() - values.get(index - 1).getColor().getH();
\r
131 else if (angle < -180.f)
\r
133 h = values.get(index - 1).getColor().getH() + value * angle;
\r
139 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(),
\r
140 value * values.get(index).getColor().getV() + valuei * values.get(index - 1).getColor().getV());
\r
141 byte color[] = new byte[] { (byte) interpolated.getR(), (byte) interpolated.getG(), (byte) interpolated.getB() };
\r
149 * Returns gradient in array of bytes. Array is RGB order and int contains 3 * requested size of bytes.
\r
152 * If gradient contains only one color array is filled with that color
\r
155 * if gradient has no colors array is filled with white
\r
157 * @param size number of pixels
\r
158 * @return gradient in array of bytes
\r
160 public byte[] getGradientArray(int size) {
\r
161 byte array[] = new byte[size * 3];
\r
162 if (values.size() > 1) {
\r
163 for (int i = 0; i < size; i++) {
\r
165 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) i / (double) size;
\r
168 color = getRGBColor(value);
\r
170 color = getHSVColor(value);
\r
171 array[index] = color[0];
\r
172 array[index + 1] = color[1];
\r
173 array[index + 2] = color[2];
\r
175 } else if (values.size() == 1) {
\r
176 byte color[] = new byte[3];
\r
177 color[0] = (byte)values.get(0).getColor().getR();
\r
178 color[1] = (byte)values.get(0).getColor().getG();
\r
179 color[2] = (byte)values.get(0).getColor().getB();
\r
180 for (int i = 0; i < size; i++) {
\r
182 array[index] = color[0];
\r
183 array[index + 1] = color[1];
\r
184 array[index + 2] = color[2];
\r
187 for (int i = 0; i < size; i++) {
\r
189 array[index] = (byte)255;
\r
190 array[index + 1] = (byte)255;
\r
191 array[index + 2] = (byte)255;
\r
199 * Returns gradient in image.
\r
202 * If gradient contains only one color image is filled with that color
\r
205 * if gradient has no colors image is filled with white
\r
208 * Style must be set to <code>SWT.HORIZONTAL</code> or <code>SWT.VERTICAL</code>
\r
210 * @param size number of pixels
\r
211 * @return gradient in array of bytes
\r
214 public Image getGradientImage(int width, int height, int style) {
\r
215 Image image = new Image(Display.getCurrent(), width, height);
\r
216 GC gc = new GC(image);
\r
217 gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
\r
218 gc.fillRectangle(0, 0, width, height);
\r
219 if (values.size() > 1) {
\r
220 if (SWT.HORIZONTAL == (style | SWT.HORIZONTAL)) {
\r
221 for (int x = 0; x < width; x++) {
\r
222 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) x
\r
223 / (double) (width - 1);
\r
226 byteColor = getRGBColor(value);
\r
228 byteColor = getHSVColor(value);
\r
229 Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);
\r
230 gc.setForeground(color);
\r
231 gc.drawLine(x, 0, x, height);
\r
234 } else if (SWT.VERTICAL == (style | SWT.VERTICAL)){
\r
235 for (int y = 0; y < height; y++) {
\r
236 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) y
\r
237 / (double) (height - 1);
\r
240 byteColor = getRGBColor(value);
\r
242 byteColor = getHSVColor(value);
\r
243 Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);
\r
244 gc.setForeground(color);
\r
245 gc.drawLine(0, y, width, y);
\r
251 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
\r
253 } else if (values.size() == 1) {
\r
254 Color color = new Color(Display.getCurrent(), values.get(0).getColor().getR(), values.get(0).getColor().getG(), values.get(0).getColor().getB());
\r
255 gc.setBackground(color);
\r
256 gc.fillRectangle(0, 0, width, height);
\r
259 gc.fillRectangle(0, 0, width, height);
\r
265 public int getType() {
\r
269 public Collection<ColorValue> getColorValues() {
\r
273 public ColorValue[] getColorValueArray() {
\r
274 return values.toArray(new ColorValue[values.size()]);
\r
278 public void addCastorColorValue(ColorValue value) {
\r
280 Collections.sort(this.values, new ColorValueComparator());
\r
284 public void setCastorType(int type) {
\r
289 public int hashCode() {
\r
290 int hash = 0x58fb3;
\r
291 for (ColorValue cv : values)
\r
292 hash ^= cv.hashCode();
\r
297 public boolean equals(Object obj) {
\r
300 if (obj.getClass() != getClass())
\r
302 ColorGradient cg = (ColorGradient) obj;
\r
303 if (cg.type != type) return false;
\r
304 if (values.size()!=cg.values.size()) return false;
\r
305 if (!values.containsAll(cg.values)) return false;
\r