/* * 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; } }