/*
* ColorSelector.java
* Select colors
* Usage:
*
* Ken Shirriff ken.shirriff@eng.sun.com
*
* Copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* Please refer to the file "license.txt"
* for further important copyright and licensing information.
*/
import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;
// Color bar draws a colored bar of the specified color
class ColorBar extends Panel {
Image img=null;
byte data[] = new byte[1];
byte rb[] = new byte[1];
byte gb[] = new byte[1];
byte bb[] = new byte[1];
ColorBar() {
data[0] = 0;
}
public void setColor(int r, int g, int b) {
rb[0] = (byte)r;
gb[0] = (byte)g;
bb[0] = (byte)b;
ColorModel cm = new IndexColorModel(8,1,rb,gb,bb);
img = createImage(new MemoryImageSource(1,1,cm,data,0,1));
setBackground(new Color(r,g,b));
repaint();
}
public void paint(Graphics gr) {
int w=size().width/2;
if (img != null) {
gr.drawImage(img,0,0,w,size().height,this);
}
}
}
public class ColorSelector extends Applet {
Scrollbar scrolls[] = new Scrollbar[6];
Label labels[] = new Label[12];
TextField fields[] = new TextField[6];
int vals[] = new int[6];
int toph = 20;
String status = "ColorSelector by Ken Shirriff";
ColorModel cm=null;
ColorInput input = new RBW();
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints gc = new GridBagConstraints();
ColorBar cb = new ColorBar();
int update=0;
TextField hex=null;
public void constrainAdd(Component o) {
gridbag.setConstraints(o,gc);
add(o);
}
public void init() {
// Lay out the scrollbars and stuff
setLayout(gridbag);
String inputlabels[] = defaultlabels();
gc.fill = GridBagConstraints.HORIZONTAL;
gc.anchor = GridBagConstraints.NORTH;
for (int i=0;i<6;i++) {
if (i==3) {
inputlabels = input.labels();
Panel p = new Panel();
gc.gridwidth = GridBagConstraints.REMAINDER;
constrainAdd(p);
} else {
}
scrolls[i] = new Scrollbar(Scrollbar.HORIZONTAL,0,20,0,255);
labels[2*i] = new Label(inputlabels[(i%3)*2]);
labels[2*i+1] = new Label(inputlabels[(i%3)*2+1]);
fields[i] = new TextField("0",3);
gc.gridwidth = 1;
constrainAdd(labels[2*i]);
gc.weightx = 1;
constrainAdd(scrolls[i]);
gc.weightx = 0;
gc.gridwidth = GridBagConstraints.RELATIVE;
constrainAdd(labels[2*i+1]);
gc.gridwidth = GridBagConstraints.REMAINDER;
constrainAdd(fields[i]);
}
Choice ch = new Choice();
ch.addItem("RBW");
ch.addItem("HSV");
ch.addItem("RGYB");
ch.addItem("HLS");
ch.addItem("YIQ");
gc.gridwidth = 1;
constrainAdd(ch);
hex = new TextField(8);
gc.gridwidth = GridBagConstraints.RELATIVE;
gc.fill = GridBagConstraints.BOTH;
gc.ipadx = 40;
constrainAdd(cb);
gc.gridwidth = GridBagConstraints.REMAINDER;
constrainAdd(hex);
update=1;
repaint();
}
public String[] defaultlabels() {
String s[] = new String[6];
s[0] = "Red";
s[1] = "";
s[2] = "Green";
s[3] = "";
s[4] = "Blue";
s[5] = "";
return s;
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics gr) {
if (update==1) {
updateScrollRGB();
} else if (update==2) {
updateScrollOther();
} else if (update==3) {
updateTextRGB();
} else if (update==4) {
updateTextOther();
} else if (update==5) {
updateHex();
}
update=0;
}
public void updateScrollRGB() {
for (int i=0;i<3;i++) {
vals[i] = scrolls[i].getValue();
}
input.fromRGB(vals);
updateStuff();
}
public void updateScrollOther() {
for (int i=3;i<6;i++) {
vals[i] = scrolls[i].getValue();
}
input.toRGB(vals);
updateStuff();
}
public void updateTextRGB() {
for (int i=0;i<3;i++) {
vals[i] = Integer.parseInt(fields[i].getText());
}
input.fromRGB(vals);
updateStuff();
}
public void updateTextOther() {
for (int i=3;i<6;i++) {
vals[i] = Integer.parseInt(fields[i].getText());
}
input.toRGB(vals);
updateStuff();
}
public void updateHex() {
int r,g,b;
try {
String s = hex.getText();
if (s.substring(0,2).equals("0x")) {
s = s.substring(2);
}
r = Integer.parseInt(s.substring(0,2),16);
g = Integer.parseInt(s.substring(2,4),16);
b = Integer.parseInt(s.substring(4,6),16);
vals[0] = r;
vals[1] = g;
vals[2] = b;
input.fromRGB(vals);
} catch (Exception e) {
}
updateStuff();
}
public void updateStuff() {
// Update text fields and scrollbars
for (int i=0;i<6;i++) {
fields[i].setText(""+vals[i]);
}
for (int i=0;i<6;i++) {
scrolls[i].setValue(vals[i]);
}
// Update color
byte rc[] = new byte[2];
byte gc[] = new byte[2];
byte bc[] = new byte[2];
rc[0] = (byte)vals[0];
gc[0] = (byte)vals[1];
bc[0] = (byte)vals[2];
rc[1] = (byte)200;
gc[1] = (byte)100;
bc[1] = (byte)200;
cb.setColor(vals[0], vals[1], vals[2]);
// Update hex string
int color=vals[0]*65536+vals[1]*256+vals[2];
String s = Integer.toString(color,16);
String p = "0x000000";
hex.setText(p.substring(0,8-s.length())+s);
}
void newsliders() {
String inputlabels[] = input.labels();
for (int i=0;i<3;i++) {
labels[2*i+6].setText(inputlabels[i*2]);
labels[2*i+7].setText(inputlabels[i*2+1]);
}
update=1;
layout();
super.layout();
repaint();
}
public synchronized boolean handleEvent(Event e) {
if (e.target instanceof Scrollbar) {
int num = -1;
for (int i=0;i<6;i++) {
if (e.target.equals(scrolls[i])) {
num = i;
break;
}
}
if (num>=0 && num<3) {
update=1;
repaint();
} else if (num>=3 && num<6) {
update=2;
repaint();
}
}
return super.handleEvent(e);
}
public synchronized boolean action(Event e, Object o) {
if (e.target instanceof Choice) {
String nam = (String)o;
try {
Class c = Class.forName(nam);
input = (ColorInput) c.newInstance();
} catch (Exception ex) {
System.out.println("Couldn't load "+o);
}
newsliders();
} else if (e.target instanceof TextField) {
int num = -1;
if (e.target.equals(hex)) {
update=5;
repaint();
return true;
}
for (int i=0;i<6;i++) {
if (e.target.equals(fields[i])) {
num = i;
break;
}
}
if (num>=0 && num<3) {
update=3;
repaint();
} else if (num>=3 && num<6) {
update=4;
repaint();
}
}
return true;
}
public String getAppletInfo() {
return "ColorSelector by Ken Shirriff shirriff@eng.sun.com";
}
public String[][] getParameterInfo() {
String [][] info = {
{"width ","int ", "image width"},
{"height ","int ", "image height"},
};
return info;
}
}
abstract class ColorInput {
abstract void toRGB(int vals[]);
abstract void fromRGB(int vals[]);
abstract String[] labels();
final int RED =0;
final int GRN =1;
final int BLU =2;
public double abs(double v) {
if (v>0) {
return v;
} else {
return -v;
}
}
public double max(double a, double b) {
if (a>b) {
return a;
} else {
return b;
}
}
public double min(double a, double b) {
if (aabs(r)) {
f = 1-abs(r/b);
} else {
f = abs(b/r)-1;
}
if (r>=0 && b<=0) {
h = .5+f/2;
} else if (r<=0 && b<=0) {
h = 1.5-f/2;
} else if (r<=0 && b>=0) {
h = 3+f;
} else {
h = 5-f;
}
}
h /= 6.;
/*
* Figure out the value and saturation.
*/
c = max(abs(r),abs(b));
if (w>=0) {
v = (w+1+c*(1-w))/2;
vs = c*(2-w)/2;
} else {
v = (w+1+c)/2;
vs = c*(w+2)/2;
}
if (v==0) {
s = 0;
} else {
s = vs/v;
}
vals2[HUE] = (int)(h*255.);
vals2[SAT] = (int)(s*255.);
vals2[VAL] = (int)(v*255.);
hsv = new HSV();
hsv.toRGB(vals2);
vals[0] = vals2[0];
vals[1] = vals2[1];
vals[2] = vals2[2];
}
public void fromRGB(int vals[]) {
hsv = new HSV();
hsv.fromRGB(vals);
double h,s,v;
double a,k,t,c,vs,f=0,sr=0,sb=0,r,b;
h = vals[HUE]/255.;
s = vals[SAT]/255.;
v = vals[VAL]/255.;
vs=v*s;
if (v-.5>vs/2) {
a = 1.5+vs-v;
k = 1;
} else {
a = .5+v;
k = -1;
}
t = Math.sqrt(a*a-2*vs);
c = a-t;
k = k*(2-a-t);
h = h*6;
/*
* Figure out the hue.
*/
if (h <= 1) {
sr = 1;
sb = -1;
f = 2*h-1;
} else if (h <= 2) {
sr = -1;
sb = -1;
f = 3-2*h;
} else if (h <= 4) {
sr = -1;
sb = 1;
f = h-3;
} else if (h <= 6) {
sr = 1;
sb = 1;
f = 5-h;
}
if (f>=0) {
b = sb;
r = (1-f)*sr;
} else {
r = sr;
b = (f+1)*sb;
}
vals[R] = (int)((r*c/2+.5)*255);
vals[B] = (int)((b*c/2+.5)*255);
vals[W] = (int)(((k+1)/2)*255);
}
public String[] labels() {
String s[] = new String[6];
s[0] = "Green";
s[1] = "Red";
s[2] = "Yellow";
s[3] = "Blue";
s[4] = "Black";
s[5] = "White";
return s;
}
}
class RGYB extends ColorInput {
final int RG =3;
final int YB =4;
final int BW =5;
public void toRGB(int vals[]) {
double rg, yb, bw;
double r,g,b;
rg = vals[RG]/255.;
yb = vals[YB]/255.;
bw = vals[BW]/255.;
r = rg*bw*2;
g = yb*bw*2;
b = (1-max(rg,yb))*bw*2;
r = clip(r);
g = clip(g);
b = clip(b);
vals[RED] = (int)(r*255);
vals[GRN] = (int)(g*255);
vals[BLU] = (int)(b*255);
}
public void fromRGB(int vals[]) {
double r,g,b;
double rg=0, yb=0, bw;
r = vals[RED]/255.;
g = vals[GRN]/255.;
b = vals[BLU]/255.;
if (r>g) {
bw = (r+b)/2;
} else {
bw = (g+b)/2;
}
if (bw != 0) {
rg=r/bw/2;
yb=g/bw/2;
}
vals[RG] = (int)(rg*255);
vals[YB] = (int)(yb*255);
vals[BW] = (int)(bw*255);
}
public String[] labels() {
String s[] = new String[6];
s[0] = "BG";
s[1] = "RY";
s[2] = "RB";
s[3] = "YG";
s[4] = "Black";
s[5] = "White";
return s;
}
}
class HLS extends ColorInput {
final int H =3;
final int L =4;
final int S =5;
// From Computer Graphics Principles and Practice, Foley, van Dam,
// Feiner, Hughes
public double value(double n1, double n2, double hue) {
if (hue<0) {
hue += 360;
}
if (hue>360) {
hue -= 360;
}
if (hue<60) {
return n1+(n2-n1)*hue/60.;
} else if (hue<180) {
return n2;
} else if (hue<240) {
return n1+(n2-n1)*(240-hue)/60.;
} else {
return n1;
}
}
public void toRGB(int vals[]) {
double h,l,s;
double r,g,b;
h = vals[H]/255.*360;
l = vals[L]/255.;
s = vals[S]/255.;
double m1,m2;
if (l<.5) {
m2 = l*(1+s);
} else {
m2 = l+s-l*s;
}
m1 = 2*l-m2;
if (s==0) {
r=l;
g=l;
b=l;
} else {
r=value(m1,m2,h+120);
g=value(m1,m2,h);
b=value(m1,m2,h-120);
}
vals[RED] = (int)(r*255);
vals[GRN] = (int)(g*255);
vals[BLU] = (int)(b*255);
}
public void fromRGB(int vals[]) {
double r,g,b;
double h,l,s;
r = vals[RED]/255.;
g = vals[GRN]/255.;
b = vals[BLU]/255.;
double min = min(min(r,g),b);
double max = max(max(r,g),b);
l = (max+min)/2;
if (max==min) {
s = 0;
h = 0;
} else {
if (l<=0.5) {
s = (max-min)/(max+min);
} else {
s = (max-min)/(2-max-min);
}
double delta = max-min;
if (r==max) {
h=(g-b)/delta;
} else if (g==max) {
h=2+(b-r)/delta;
} else {
h=4+(r-g)/delta;
}
h /= 6.;
if (h<0) {
h += 1;
}
}
vals[H] = (int)(h*255);
vals[L] = (int)(l*255);
vals[S] = (int)(s*255);
}
public String[] labels() {
String s[] = new String[6];
s[0] = "Hue";
s[1] = "";
s[2] = "Lightness";
s[3] = "";
s[4] = "Saturation";
s[5] = "";
return s;
}
}
class YIQ extends ColorInput {
final int Y =3;
final int I =4;
final int Q =5;
public void toRGB(int vals[]) {
double y,i,q;
double r,g,b;
y = vals[Y]/255.; // 0 to 1
i = (vals[I]/255.*2-1)*.596; // -.596 to .596
q = (vals[Q]/255.*2-1)*.522; // -.522 to .522
r = clip(y+.956*i+.623*q);
g = clip(y-.272*i-.648*q);
b = clip(y-1.105*i+.705*q);
vals[RED] = (int)(r*255);
vals[GRN] = (int)(g*255);
vals[BLU] = (int)(b*255);
}
public void fromRGB(int vals[]) {
double r,g,b;
double y,i,q;
r = vals[RED]/255.;
g = vals[GRN]/255.;
b = vals[BLU]/255.;
y = .299*r+.587*g+.114*b;
i = .596*r-.274*g-.322*b;
q = .211*r-.522*g+.311*b;
i = (i/.596+1)/2; // 0 to 1
q = (q/.522+1)/2; // 0 to 1
vals[Y] = (int)(y*255);
vals[I] = (int)(i*255);
vals[Q] = (int)(q*255);
}
public String[] labels() {
String s[] = new String[6];
s[0] = "Y";
s[1] = "";
s[2] = "I";
s[3] = "";
s[4] = "Q";
s[5] = "";
return s;
}
}