Sync git svn branch with SVN repository r33269.
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / color / ColorGradient.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.utils.ui.color;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.Collections;\r
17 import java.util.List;\r
18 \r
19 import org.eclipse.swt.SWT;\r
20 import org.eclipse.swt.graphics.Color;\r
21 import org.eclipse.swt.graphics.GC;\r
22 import org.eclipse.swt.graphics.Image;\r
23 import org.eclipse.swt.widgets.Display;\r
24 \r
25 /**\r
26  * \r
27  * <p>\r
28  * Color gradient between multiple colors\r
29  * </p>\r
30  * <p>\r
31  * Supports RGB and HSV linear interpolation between colors\r
32  * </p>\r
33  * \r
34  * @author Marko Luukkainen\r
35  *\r
36  */\r
37 public class ColorGradient {\r
38     public static final int RGB = 0;\r
39 \r
40     public static final int HSV = 1;\r
41 \r
42     protected ArrayList<ColorValue> values;\r
43 \r
44     protected int type;\r
45     \r
46     public ColorGradient() {\r
47         this.values = new ArrayList<ColorValue>();\r
48         this.type = RGB;\r
49     }\r
50 \r
51     public ColorGradient(ColorGradient copyFrom) {\r
52         this.values = new ArrayList<ColorValue>(copyFrom.values);\r
53         this.type = copyFrom.type;\r
54     }\r
55 \r
56     public ColorGradient(Collection<ColorValue> values) {\r
57         this.values = new ArrayList<ColorValue>(values);\r
58         Collections.sort(this.values, new ColorValueComparator());\r
59         this.type = RGB;\r
60     }\r
61 \r
62     public ColorGradient(ColorValue array[]) {\r
63         this.values = new ArrayList<ColorValue>(array.length);\r
64         for (ColorValue c : array) {\r
65             values.add(c);\r
66         }\r
67         Collections.sort(this.values, new ColorValueComparator());\r
68         this.type = RGB;\r
69     }\r
70 \r
71     public ColorGradient(Collection<ColorValue> values, int type) {\r
72         this.values = new ArrayList<ColorValue>(values);\r
73         Collections.sort(this.values, new ColorValueComparator());\r
74         this.type = type;\r
75     }\r
76 \r
77     public ColorGradient(ColorValue array[], int type) {\r
78         this.values = new ArrayList<ColorValue>();\r
79         for (ColorValue c : array) {\r
80             values.add(c);\r
81         }\r
82         Collections.sort(this.values, new ColorValueComparator());\r
83         this.type = type;\r
84     }\r
85 \r
86     /**\r
87      * Interpolates color in RGB space\r
88      * \r
89      * @param value\r
90      * @return\r
91      */\r
92     private byte[] getRGBColor(double value) {\r
93         int index = 1;\r
94         while (values.get(index).getValue() <= value && index < values.size()-1)\r
95             index++;\r
96 \r
97         value -= values.get(index - 1).getValue();\r
98         value /= (values.get(index).getValue() - values.get(index - 1).getValue());\r
99         double valuei = 1.0 - value;\r
100         byte color[] = new byte[] {\r
101                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getR() + valuei * values.get(index - 1).getColor().getR())),\r
102                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getG() + valuei * values.get(index - 1).getColor().getG())),\r
103                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getB() + valuei * values.get(index - 1).getColor().getB())) };\r
104         return color;\r
105 \r
106     }\r
107 \r
108     /**\r
109      * Interpolates color in HSV space\r
110      * \r
111      * @param value\r
112      * @return\r
113      */\r
114     private byte[] getHSVColor(double value) {\r
115         int index = 1;\r
116         while (values.get(index).getValue() <= value && index < values.size()-1)\r
117             index++;\r
118 \r
119         value -= values.get(index - 1).getValue();\r
120         value /= (values.get(index).getValue() - values.get(index - 1).getValue());\r
121         double valuei = 1.0 - value;\r
122         double h;\r
123         if (Float.isNaN(values.get(index).getColor().getH())) {\r
124             h = values.get(index-1).getColor().getH();\r
125         } else if (Float.isNaN(values.get(index-1).getColor().getH())) {\r
126             h = values.get(index).getColor().getH();\r
127         } else {\r
128             // selecting shortest direction between hues\r
129             float angle = values.get(index).getColor().getH() - values.get(index - 1).getColor().getH();\r
130             if (angle > 180.f)\r
131                 angle -= 360.f;\r
132             else if (angle < -180.f)\r
133                 angle += 360.f;\r
134             h = values.get(index - 1).getColor().getH() + value * angle;\r
135             if (h > 360.f)\r
136                 h -= 360.f;\r
137             else if (h < 0.f)\r
138                 h+= 360.f;\r
139         }\r
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(),\r
141                 value * values.get(index).getColor().getV() + valuei * values.get(index - 1).getColor().getV());\r
142         byte color[] = new byte[] { (byte) interpolated.getR(), (byte) interpolated.getG(), (byte) interpolated.getB() };\r
143 \r
144         return color;\r
145 \r
146     }\r
147 \r
148     /**\r
149      * <p>\r
150      * Returns gradient in array of bytes. Array is RGB order and int contains 3 * requested size of bytes.\r
151      * </p>\r
152      * <p>\r
153      * If gradient contains only one color array is filled with that color\r
154      * </p>\r
155      * <p>\r
156      * if gradient has no colors array is filled with white\r
157      * </p>\r
158      * @param size number of pixels\r
159      * @return gradient in array of bytes\r
160      */\r
161     public byte[] getGradientArray(int size) {\r
162         byte array[] = new byte[size * 3];\r
163         if (values.size() > 1) {\r
164             for (int i = 0; i < size; i++) {\r
165                 int index = i * 3;\r
166                 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) i / (double) size;\r
167                 byte color[];\r
168                 if (type == RGB)\r
169                     color = getRGBColor(value);\r
170                 else\r
171                     color = getHSVColor(value);\r
172                 array[index] = color[0];\r
173                 array[index + 1] = color[1];\r
174                 array[index + 2] = color[2];\r
175             }\r
176         } else if (values.size() == 1) {\r
177             byte color[] = new byte[3];\r
178             color[0] = (byte)values.get(0).getColor().getR();\r
179             color[1] = (byte)values.get(0).getColor().getG();\r
180             color[2] = (byte)values.get(0).getColor().getB();\r
181             for (int i = 0; i < size; i++) {\r
182                 int index = i * 3;\r
183                 array[index] = color[0];\r
184                 array[index + 1] = color[1];\r
185                 array[index + 2] = color[2];\r
186             }\r
187         } else {\r
188             for (int i = 0; i < size; i++) {\r
189                 int index = i * 3;\r
190                 array[index] = (byte)255;\r
191                 array[index + 1] = (byte)255;\r
192                 array[index + 2] = (byte)255;\r
193             }\r
194         }\r
195         return array;\r
196     }\r
197 \r
198     /**\r
199      * <p>\r
200      * Returns gradient in image.\r
201      * </p>\r
202      * <p>\r
203      * If gradient contains only one color image is filled with that color\r
204      * </p>\r
205      * <p>\r
206      * if gradient has no colors image is filled with white\r
207      * </p>\r
208      * <p>\r
209      * Style must be set to  <code>SWT.HORIZONTAL</code> or <code>SWT.VERTICAL</code>\r
210      * </p>\r
211      * @param size number of pixels\r
212      * @return gradient in array of bytes\r
213      */\r
214     \r
215     public Image getGradientImage(int width, int height, int style) {\r
216         Image image = new Image(Display.getCurrent(), width, height);\r
217         GC gc = new GC(image);\r
218         gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));\r
219         gc.fillRectangle(0, 0, width, height);\r
220         if (values.size() > 1) {\r
221             if (SWT.HORIZONTAL == (style | SWT.HORIZONTAL)) {\r
222                 for (int x = 0; x < width; x++) {\r
223                     double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) x\r
224                             / (double) (width - 1);\r
225                     byte byteColor[];\r
226                     if (type == RGB)\r
227                         byteColor = getRGBColor(value);\r
228                     else\r
229                         byteColor = getHSVColor(value);\r
230                     Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);\r
231                     gc.setForeground(color);\r
232                     gc.drawLine(x, 0, x, height);\r
233                     color.dispose();\r
234                 }\r
235             } else if (SWT.VERTICAL == (style | SWT.VERTICAL)){\r
236                 for (int y = 0; y < height; y++) {\r
237                     double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) y\r
238                             / (double) (height - 1);\r
239                     byte byteColor[];\r
240                     if (type == RGB)\r
241                         byteColor = getRGBColor(value);\r
242                     else\r
243                         byteColor = getHSVColor(value);\r
244                     Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);\r
245                     gc.setForeground(color);\r
246                     gc.drawLine(0, y, width, y);\r
247                     color.dispose();\r
248                 }\r
249             } else {\r
250                 gc.dispose();\r
251                 image.dispose();\r
252                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);\r
253             }\r
254         } else if (values.size() == 1) {\r
255             Color color = new Color(Display.getCurrent(), values.get(0).getColor().getR(), values.get(0).getColor().getG(), values.get(0).getColor().getB());      \r
256             gc.setBackground(color);\r
257             gc.fillRectangle(0, 0, width, height);\r
258             color.dispose();\r
259         } else {\r
260             gc.fillRectangle(0, 0, width, height);\r
261         }\r
262         gc.dispose();\r
263         return image;\r
264     }\r
265     \r
266     public int getType() {\r
267         return type;\r
268     }\r
269     \r
270     public List<ColorValue> getColorValues() {\r
271         return values;\r
272     }\r
273     \r
274     public ColorValue[] getColorValueArray() {\r
275         return values.toArray(new ColorValue[values.size()]);\r
276     }\r
277     \r
278     @Deprecated\r
279     public void addCastorColorValue(ColorValue value) {\r
280         values.add(value);\r
281         Collections.sort(this.values, new ColorValueComparator());\r
282     }\r
283     \r
284     @Deprecated\r
285     public void setCastorType(int type) {\r
286         this.type = type;\r
287     }\r
288     \r
289     @Override\r
290     public int hashCode() {\r
291         int hash = 0x58fb3;\r
292         for (ColorValue cv : values)\r
293             hash ^= cv.hashCode();\r
294         return hash;\r
295     }\r
296     \r
297     @Override\r
298     public boolean equals(Object obj) {\r
299         if (obj == null)\r
300                 return false;\r
301         if (obj.getClass() != getClass())\r
302                 return false;\r
303         ColorGradient cg = (ColorGradient) obj;\r
304         if (cg.type != type) return false;\r
305         if (values.size()!=cg.values.size()) return false;\r
306         if (!values.containsAll(cg.values)) return false;\r
307         return true;\r
308     }\r
309 \r
310 }\r