/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.utils.ui.color; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; /** * *

* Color gradient between multiple colors *

*

* Supports RGB and HSV linear interpolation between colors *

* * @author Marko Luukkainen * */ public class ColorGradient { public static final int RGB = 0; public static final int HSV = 1; protected ArrayList values; protected int type; public ColorGradient() { this.values = new ArrayList(); this.type = RGB; } public ColorGradient(ColorGradient copyFrom) { this.values = new ArrayList(copyFrom.values); this.type = copyFrom.type; } public ColorGradient(Collection values) { this.values = new ArrayList(values); Collections.sort(this.values, new ColorValueComparator()); this.type = RGB; } public ColorGradient(ColorValue array[]) { this.values = new ArrayList(array.length); for (ColorValue c : array) { values.add(c); } Collections.sort(this.values, new ColorValueComparator()); this.type = RGB; } public ColorGradient(Collection values, int type) { this.values = new ArrayList(values); Collections.sort(this.values, new ColorValueComparator()); this.type = type; } public ColorGradient(ColorValue array[], int type) { this.values = new ArrayList(); for (ColorValue c : array) { values.add(c); } Collections.sort(this.values, new ColorValueComparator()); this.type = type; } /** * Interpolates color in RGB space * * @param value * @return */ private byte[] getRGBColor(double value) { int index = 1; while (values.get(index).getValue() <= value && index < values.size()-1) index++; value -= values.get(index - 1).getValue(); value /= (values.get(index).getValue() - values.get(index - 1).getValue()); double valuei = 1.0 - value; byte color[] = new byte[] { (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getR() + valuei * values.get(index - 1).getColor().getR())), (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getG() + valuei * values.get(index - 1).getColor().getG())), (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getB() + valuei * values.get(index - 1).getColor().getB())) }; return color; } /** * Interpolates color in HSV space * * @param value * @return */ private byte[] getHSVColor(double value) { int index = 1; while (values.get(index).getValue() <= value && index < values.size()-1) index++; value -= values.get(index - 1).getValue(); value /= (values.get(index).getValue() - values.get(index - 1).getValue()); double valuei = 1.0 - value; double h; if (Float.isNaN(values.get(index).getColor().getH())) { h = values.get(index-1).getColor().getH(); } else if (Float.isNaN(values.get(index-1).getColor().getH())) { h = values.get(index).getColor().getH(); } else { // selecting shortest direction between hues float angle = values.get(index).getColor().getH() - values.get(index - 1).getColor().getH(); if (angle > 180.f) angle -= 360.f; else if (angle < -180.f) angle += 360.f; h = values.get(index - 1).getColor().getH() + value * angle; if (h > 360.f) h -= 360.f; else if (h < 0.f) h+= 360.f; } 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(), value * values.get(index).getColor().getV() + valuei * values.get(index - 1).getColor().getV()); byte color[] = new byte[] { (byte) interpolated.getR(), (byte) interpolated.getG(), (byte) interpolated.getB() }; return color; } /** *

* Returns gradient in array of bytes. Array is RGB order and int contains 3 * requested size of bytes. *

*

* If gradient contains only one color array is filled with that color *

*

* if gradient has no colors array is filled with white *

* @param size number of pixels * @return gradient in array of bytes */ public byte[] getGradientArray(int size) { byte array[] = new byte[size * 3]; if (values.size() > 1) { for (int i = 0; i < size; i++) { int index = i * 3; double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) i / (double) size; byte color[]; if (type == RGB) color = getRGBColor(value); else color = getHSVColor(value); array[index] = color[0]; array[index + 1] = color[1]; array[index + 2] = color[2]; } } else if (values.size() == 1) { byte color[] = new byte[3]; color[0] = (byte)values.get(0).getColor().getR(); color[1] = (byte)values.get(0).getColor().getG(); color[2] = (byte)values.get(0).getColor().getB(); for (int i = 0; i < size; i++) { int index = i * 3; array[index] = color[0]; array[index + 1] = color[1]; array[index + 2] = color[2]; } } else { for (int i = 0; i < size; i++) { int index = i * 3; array[index] = (byte)255; array[index + 1] = (byte)255; array[index + 2] = (byte)255; } } return array; } /** *

* Returns gradient in image. *

*

* If gradient contains only one color image is filled with that color *

*

* if gradient has no colors image is filled with white *

*

* Style must be set to SWT.HORIZONTAL or SWT.VERTICAL *

* @param size number of pixels * @return gradient in array of bytes */ public Image getGradientImage(int width, int height, int style) { Image image = new Image(Display.getCurrent(), width, height); GC gc = new GC(image); gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); gc.fillRectangle(0, 0, width, height); if (values.size() > 1) { if (SWT.HORIZONTAL == (style | SWT.HORIZONTAL)) { for (int x = 0; x < width; x++) { double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) x / (double) (width - 1); byte byteColor[]; if (type == RGB) byteColor = getRGBColor(value); else byteColor = getHSVColor(value); Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff); gc.setForeground(color); gc.drawLine(x, 0, x, height); color.dispose(); } } else if (SWT.VERTICAL == (style | SWT.VERTICAL)){ for (int y = 0; y < height; y++) { double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) y / (double) (height - 1); byte byteColor[]; if (type == RGB) byteColor = getRGBColor(value); else byteColor = getHSVColor(value); Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff); gc.setForeground(color); gc.drawLine(0, y, width, y); color.dispose(); } } else { gc.dispose(); image.dispose(); SWT.error(SWT.ERROR_INVALID_ARGUMENT); } } else if (values.size() == 1) { Color color = new Color(Display.getCurrent(), values.get(0).getColor().getR(), values.get(0).getColor().getG(), values.get(0).getColor().getB()); gc.setBackground(color); gc.fillRectangle(0, 0, width, height); color.dispose(); } else { gc.fillRectangle(0, 0, width, height); } gc.dispose(); return image; } public int getType() { return type; } public Collection getColorValues() { return values; } public ColorValue[] getColorValueArray() { return values.toArray(new ColorValue[values.size()]); } @Deprecated public void addCastorColorValue(ColorValue value) { values.add(value); Collections.sort(this.values, new ColorValueComparator()); } @Deprecated public void setCastorType(int type) { this.type = type; } @Override public int hashCode() { int hash = 0x58fb3; for (ColorValue cv : values) hash ^= cv.hashCode(); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj.getClass() != getClass()) return false; ColorGradient cg = (ColorGradient) obj; if (cg.type != type) return false; if (values.size()!=cg.values.size()) return false; if (!values.containsAll(cg.values)) return false; return true; } }