home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / BasicHSVChooserPanel.java < prev    next >
Text File  |  1998-02-26  |  19KB  |  685 lines

  1. /*
  2.  * @(#)BasicHSVChooserPanel.java    1.5 98/02/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import com.sun.java.swing.plaf.ComponentUI;
  26. import com.sun.java.swing.plaf.ColorChooserUI;
  27. import java.awt.*;
  28. import java.awt.image.*;
  29. import java.awt.event.*;
  30. import java.beans.PropertyChangeEvent;
  31. import java.beans.PropertyChangeListener;
  32. import java.io.Serializable;
  33.  
  34.  
  35. /**
  36.  * The standard HSV chooser.
  37.  * <p>
  38.  * Warning: serialized objects of this class will not be compatible with
  39.  * future swing releases.  The current serialization support is appropriate 
  40.  * for short term storage or RMI between Swing1.0 applications.  It will
  41.  * not be possible to load serialized Swing1.0 objects with future releases
  42.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  43.  * baseline for the serialized form of Swing objects.
  44.  *
  45.  * @version 1.5 02/02/98
  46.  * @author James Gosling
  47.  * @author Amy Fowler
  48.  * @author Tom Santos
  49.  */
  50. public class BasicHSVChooserPanel extends ColorChooserPanel implements PropertyChangeListener, Serializable {
  51.     // Note: layout manager code was lifted directly from Jame's
  52.     // original color chooser; it really needs to be re-written
  53.     // to handle sizing better and to NOT rely on the order of
  54.     // children added in laying things out.
  55.     //
  56.  
  57.     static int preferredWidth = 300;
  58.     static int preferredHeight = 200;
  59.     static Dimension preferredSize = new Dimension(preferredWidth, preferredHeight);
  60.     static int butGap = 2;
  61.  
  62.     protected Spinner hue, saturation, brightness;
  63.     protected ColorPatch resultColor;
  64.     protected HueLightnessPatch hlp;
  65.     protected ColorWheel wheel;
  66.  
  67.     Color color = Color.red;
  68.  
  69.     public BasicHSVChooserPanel() {
  70.         super();
  71.     }
  72.  
  73.     public BasicHSVChooserPanel( Color startColor ) {
  74.         color = startColor;
  75.     }    
  76.  
  77.     public int[] getHSBColor() {
  78.         Color color = getColor();
  79.     int[] result = new int[ 3 ];
  80.         float[] hsb = Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), null );
  81.  
  82.     result[ 0 ] = (int)(hsb[ 0 ] * 100 + 0.5);
  83.     result[ 1 ] = (int)(hsb[ 1 ] * 100 + 0.5);
  84.     result[ 2 ] = (int)(hsb[ 2 ] * 100 + 0.5);
  85.  
  86.     return result;
  87.     }
  88.  
  89.     public Color getColor() {
  90.         return color;
  91.     }
  92.  
  93.     public void setColor( Color newColor ) {
  94.         color = newColor;
  95.     // Set my variables
  96.     float[] hsbColor = Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), null );
  97.     int rgbColor = Color.HSBtoRGB( hsbColor[ 0 ], hsbColor[ 1 ], hsbColor[ 2 ] );
  98.     hlp.setColor( rgbColor );
  99.     setSpinners( hsbColor );
  100.     wheel.setTheta( hsbColor[ 0 ] * 360 + 0.5 );
  101.     resultColor.setColor( rgbColor );
  102.     }
  103.        
  104.     /**
  105.      * The background color, foreground color, and font are already set to the
  106.      * defaults from the defaults table before this method is called.
  107.      */                                    
  108.     public void installChooserPanel() {
  109.         // Currently this UI implementation really hard-codes
  110.         // and depends-on all these subcomponents, therefore,
  111.         // for now, we won't break these out into overridable
  112.         // subcomponent creation methods.
  113.  
  114.         setLayout(new ColorChooserLayout());
  115.       
  116.     int[] hsb = getHSBColor();
  117.  
  118.         // Create right-hand color patch to display current color
  119.     resultColor = new ColorPatch();
  120.         resultColor.setBorder(UIManager.getBorder("ColorChooser.selectedColorBorder"));
  121.         //resultColor.addPropertyChangeListener(this);
  122.  
  123.         // Create spinners to display HSB
  124.         hue = new Spinner(hsb[ 0 ], "\u00B0 H");
  125.     hue.setMinimum(0);
  126.     hue.setMaximum(100);
  127.  
  128.         saturation = new Spinner(hsb[ 1 ], "% S");
  129.     saturation.setMinimum(0);
  130.     saturation.setMaximum(100);
  131.  
  132.     brightness = new Spinner(hsb[ 2 ], "% B");
  133.     brightness.setMinimum(0);
  134.     brightness.setMaximum(100);
  135.  
  136.         // Create hue lightness patch (in center of color wheel)
  137.     hlp = new HueLightnessPatch(resultColor, hue, saturation, brightness);
  138.  
  139.         // Note: Order of added children critical to layout manager!
  140.     add(hlp); // child0
  141.  
  142.     saturation.addAdjustmentListener(hlp);
  143.     brightness.addAdjustmentListener(hlp);
  144.  
  145.         // Create color wheel
  146.     wheel = new ColorWheel(hlp, hue);
  147.         hue.setWrap(true);
  148.         add(wheel); // child1
  149.       
  150.     add(resultColor); //child2
  151.     
  152.     add(hue); //child3
  153.     add(saturation); //child4
  154.     add(brightness); //child5 
  155.  
  156.         resultColor.addPropertyChangeListener( this );
  157.     }
  158.  
  159.     public void uninstallChooserPanel() {
  160.         resultColor.removePropertyChangeListener( this );
  161.  
  162.         setLayout(null);
  163.         remove(hlp);
  164.         remove(wheel);
  165.         remove(resultColor);
  166.         remove(hue);
  167.         remove(saturation);
  168.         remove(brightness);
  169.         hlp = null;
  170.         wheel = null;
  171.         resultColor = null;
  172.         hue = saturation = brightness = null;
  173.     }
  174.  
  175.     public void propertyChange(PropertyChangeEvent e) {
  176.     if ( e.getSource() == resultColor && e.getPropertyName().equals( "color" ) ) {
  177.         Color newColor = (Color)e.getNewValue();
  178.         Color oldColor = getColor();
  179.         fireColorPropertyChange( oldColor, newColor );
  180.     }
  181.     }
  182.  
  183.     protected void setSpinners(float hsb[]) {
  184.         float hueValue = hue.getValue();
  185.         float satValue = saturation.getValue();
  186.         float brightValue = brightness.getValue();
  187.         if (hueValue != hsb[0]) {
  188.         hue.setValue((int) (hsb[0] * 360 + 0.5));
  189.         }
  190.         if (satValue != hsb[1]) {
  191.         saturation.setValue((int) (hsb[1] * 100 + 0.5));
  192.         }
  193.         if (brightValue != hsb[2]) {
  194.         brightness.setValue((int) (hsb[2] * 100 + 0.5));
  195.         }
  196.     }
  197.  
  198.     class ColorChooserLayout implements LayoutManager, Serializable {
  199.  
  200.         public void addLayoutComponent(String name, Component comp) { }
  201.         public void removeLayoutComponent(Component comp) { }
  202.  
  203.         public Dimension preferredLayoutSize(Container cont) {
  204.         return preferredSize;
  205.         }
  206.  
  207.         public Dimension minimumLayoutSize(Container cont) {
  208.             return preferredLayoutSize(cont);
  209.         }
  210.  
  211.         public void layoutContainer(Container cont) {
  212.         int ncomp = cont.getComponentCount();
  213.         int butWidth = 16;
  214.         Rectangle r = cont.getBounds();
  215.             Insets insets = cont.getInsets();
  216.         int h = r.height - insets.top - insets.bottom;
  217.         int w = r.width - insets.left - insets.right;
  218.             int xoffset = insets.left;
  219.             int yoffset = insets.top;
  220.  
  221.         for (int i = 3; i < ncomp; i++) {
  222.             Dimension dim = cont.getComponent(i).getMinimumSize();
  223.             if (dim.width > butWidth)
  224.             butWidth = dim.width;
  225.         }
  226.             // compute diameter for color wheel
  227.         int diameter = h;
  228.  
  229.         int r3 = w - butWidth - butGap;
  230.         if (r3 < diameter)
  231.             diameter = r3;
  232.         butWidth = w - diameter - butGap;
  233.  
  234.         // inside saturation/lightness square
  235.         Component c = cont.getComponent(0);
  236.         r3 = (int) (diameter * (.75 * .71));
  237.         c.setBounds(xoffset + ((diameter - r3) >> 1), yoffset + ((diameter - r3) >> 1), 
  238.                         r3, r3);
  239.  
  240.         // outside wheel
  241.         c = cont.getComponent(1);
  242.         c.setBounds(xoffset, yoffset + ((h - diameter) >> 1), diameter, diameter);
  243.  
  244.         int y = 0;
  245.         for (int i = 3; i < ncomp; i++) {
  246.             c = cont.getComponent(i);
  247.             int dh = c.getMinimumSize().height;
  248.             c.setBounds(xoffset + w - butWidth, yoffset + y, butWidth, dh);
  249.             y += dh + butGap;
  250.         }
  251.  
  252.         // colorPatch on the side
  253.         c = cont.getComponent(2);
  254.         c.setBounds(xoffset + w - butWidth, yoffset + y, butWidth, h - y);
  255.         }
  256.     }
  257. }
  258.  
  259.  
  260. class ImageComponent extends JComponent implements ImageObserver {
  261.     protected Image img;
  262.     private boolean imgKnown;
  263.     protected Dimension isize = new Dimension(-1, -1);
  264.     protected Dimension prefsize = new Dimension(0, 0);
  265.     boolean fixedSize = false;
  266.     long lastUpdateTime = 0;
  267.  
  268.     public ImageComponent () {
  269.     super();
  270.     }
  271.     public ImageComponent (Image img) {
  272.     super();
  273.     setImage(img);
  274.     }
  275.     public ImageComponent (String name) {
  276.     super();
  277.     setImage(getToolkit().getImage(name));
  278.     }
  279.     public void setSize(int w, int h) {
  280.     if (w != isize.width || h != isize.height) {
  281.         isize.width = w;
  282.         isize.height = h;
  283.         invalidate();
  284.     }
  285.     }
  286.     public void setBounds(int x, int y, int width, int height) {
  287.     super.setBounds(x, y, width, height);
  288.     setSize(width, height);
  289.     }
  290.     public void setImage(Image img) {
  291.     if (this.img == img)
  292.         return;
  293.     this.img = img;
  294.     if (img != null)
  295.         setSize(img.getWidth(this), img.getHeight(this));
  296.     if (isShowing())
  297.         repaint(10);
  298.     }
  299.     private synchronized void waitSize() {
  300.     if (img != null)
  301.         try {
  302.         while (isize.width < 0 || isize.height < 0)
  303.         wait();
  304.         } catch(InterruptedException i) {
  305.         }
  306.     }
  307.     protected boolean immediatePaint() {
  308.     return true;
  309.     }
  310.     public synchronized boolean imageUpdate(Image img,
  311.                         int infoflags,
  312.                         int x, int y,
  313.                         int width, int height) {
  314.     long t = System.currentTimeMillis();
  315.     if ( /* lastUpdateTime+500<t || */ (infoflags & (ALLBITS | FRAMEBITS)) != 0) {
  316.         if (immediatePaint()) {
  317.         Graphics g = getGraphics();
  318.         if ( g != null ) {
  319.             paint(g);
  320.             g.dispose();
  321.         }
  322.         } else
  323.         repaint( /* 1 */ 0);
  324.         lastUpdateTime = t;
  325.     }
  326.     if ((infoflags & (ERROR | ABORT)) != 0) {
  327.         img = null;
  328.         isize.width = 0;
  329.         isize.height = 0;
  330.         notifyAll();
  331.         return false;
  332.     }
  333.     if ((infoflags & WIDTH) != 0)
  334.         isize.width = img.getWidth(this);
  335.     if ((infoflags & HEIGHT) != 0)
  336.         isize.height = img.getHeight(this);
  337.     notifyAll();
  338.     return (infoflags & ALLBITS) == 0;
  339.     }
  340.     public Dimension getPreferredSize() {
  341.         Insets insets = getInsets();
  342.     waitSize();
  343.         prefsize.width = isize.width + insets.left + insets.right;
  344.         prefsize.height = isize.height + insets.top + insets.bottom;
  345.     return prefsize;
  346.     }
  347.     public Dimension getMinimumSize() {
  348.     return getPreferredSize();
  349.     }
  350.     protected void locateImage() {
  351.     };
  352.     public void paint(Graphics g) {
  353.         Insets insets = getInsets();
  354.     Image i = img;
  355.     if (i == null) {
  356.         locateImage();
  357.         if ((i = img) == null)
  358.         return;
  359.     }
  360. if ( g == null ) System.out.println( "NULL GRAPHICS" );
  361. if ( insets == null ) System.out.println( "NULL INSETS" );
  362.     g.drawImage(i, insets.left, insets.top, this);
  363.     }
  364.     public void update(Graphics g) {
  365.     paint(g);
  366.     }
  367. }
  368.  
  369. class ColorWheel extends ImageComponent implements AdjustmentListener {
  370.     private static Image img;
  371.     SyntheticImage vSrc;
  372.     int sx, sy;
  373.     private static int cursorWidth = 3;
  374.     HueLightnessPatch hlp;
  375.     Adjustable hue;
  376.  
  377.     ColorWheel (HueLightnessPatch hlp, Adjustable hue) {
  378.     super();
  379.     this.hlp = hlp;
  380.         this.hue = hue;
  381.     hue.setMinimum(0);
  382.     hue.setMaximum(359);
  383.         hue.addAdjustmentListener(this);
  384.     enableEvents(AWTEvent.MOUSE_EVENT_MASK
  385.              | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  386.     }
  387.  
  388.     protected void processMouseEvent(MouseEvent e) {
  389.     switch (e.getID()) {
  390.       case MouseEvent.MOUSE_RELEASED:
  391.         // moveSel(e.getX(), e.getY());
  392.         // if (continuous) notfyListener();
  393.         // break;
  394.       case MouseEvent.MOUSE_PRESSED:
  395.       case MouseEvent.MOUSE_DRAGGED:
  396.         moveSel(e.getX(), e.getY());
  397.     }
  398.     }
  399.  
  400.     protected void processMouseMotionEvent(MouseEvent e) {
  401.     processMouseEvent(e);
  402.     }
  403.  
  404.     public void setTheta( double newTheta ) {
  405.         lastCursorAngle = (int)newTheta;
  406.     repaint();
  407.     }
  408.  
  409.     protected void locateImage() {
  410.     if (img == null) {
  411.         final Dimension s = getSize();
  412.         vSrc = new SyntheticImage() {
  413.         {
  414.             this.width = s.width;
  415.             this.height = s.height;
  416.         }
  417.         protected void computeRow(int y, int[] row) {
  418.             int r = width >> 1;
  419.             int r2 = r * 3 / 4;
  420.             int dy = y - r;
  421.             for (int x = width; --x >= 0;) {
  422.             int dx = x - r;
  423.             int dr = (int) (Math.sqrt(dy * dy + dx * dx + dx + dy + 0.5) + 0.5);
  424.             if (dr >= r || dr <= r2)
  425.                 row[x] = 0;
  426.             else {
  427.                 float brightness = 1.0f;
  428.                 float saturation = 1.0f;
  429.                 int theta = (int) (Math.atan2(dx, dy) * (180 / Math.PI));
  430.                 if (theta < 0)
  431.                 theta += 360;
  432.                 if (dr >= r - 2 || dr <= r2 + 2) {
  433.                 int lt = theta + (360 - 45);
  434.                 if (lt >= 360)
  435.                     lt -= 360;
  436.                 if (lt >= 180)
  437.                     lt = 360 - lt;
  438.                 lt = lt - 90;
  439.                 if (dr <= r2 + 2)
  440.                     lt = -lt;
  441.                 if (lt < 0)
  442.                     brightness = 1.0f - lt / (float) (-180);
  443.                 else
  444.                     saturation = 1.0f - lt / (float) 120;
  445.                 }
  446.                 row[x] = Color.HSBtoRGB(theta / ((float) 360.0),
  447.                             saturation, brightness);
  448.             }
  449.             }
  450.         }
  451.         };
  452.         setImage(getToolkit().createImage(vSrc));
  453.     }
  454.     }
  455.     int lastCursorAngle = -1;
  456.     public void adjustmentValueChanged(AdjustmentEvent e) {
  457.     int theta = e.getValue();
  458.     hlp.setColor(Color.HSBtoRGB(theta / ((float) 360.0), 1, 1));
  459.     hlp.propogateColor();
  460.     Graphics g = getGraphics();
  461.     if (g != null) {
  462.         paintCursor(g, lastCursorAngle);
  463.         paintCursor(g, lastCursorAngle = theta);
  464.         g.dispose();
  465.     }
  466.     }
  467.     private void moveSel(int tx, int ty) {
  468.     if (tx == sx && ty == sy)
  469.         return;
  470.     sx = tx;
  471.     sy = ty;
  472.     int dx = sx - (isize.width >> 1);
  473.     int dy = sy - (isize.height >> 1);
  474.     int theta = (int) (Math.atan2(dx, dy) * (180 / Math.PI));
  475.     if (theta < 0)
  476.         theta += 360;
  477.     hue.setValue(theta);
  478.     }
  479.     public void paint(Graphics g) {
  480.     super.paint(g);
  481.     paintCursor(g, lastCursorAngle);
  482.     }
  483.     private void paintCursor(Graphics g, int theta) {
  484.     if (theta < 0)
  485.         return;
  486.     g.setColor(Color.white);
  487.     g.setXORMode(Color.black);
  488.     int xc = isize.width >> 1;    // Center
  489.     int yc = isize.height >> 1;
  490.     int xr = (int) (Math.sin(theta * (Math.PI / 180)) * xc);    // Radius vector
  491.     int yr = (int) (Math.cos(theta * (Math.PI / 180)) * yc);
  492.     g.drawArc(xc + xr - (xr >> 3) - (xc >> 4),
  493.           yc + yr - (yr >> 3) - (yc >> 4),
  494.           xc >> 3, xc >> 3, 0, 360);
  495.     }
  496.     public void update(Graphics g) {
  497.     paint(g);
  498.     }
  499. }
  500.  
  501. class HueLightnessPatch extends ImageComponent implements AdjustmentListener {
  502.     int sx, sy;
  503.     private static int cursorWidth = 3;
  504.     SyntheticImage hli;
  505.     ColorPatch target;
  506.     Adjustable hue;
  507.     Adjustable saturation;
  508.     Adjustable brightness;
  509.     int color;
  510.     HueLightnessPatch(ColorPatch target, 
  511.                       Adjustable hue, Adjustable saturation, Adjustable brightness) {
  512.     super();
  513.     this.target = target;
  514.     this.hue = hue;
  515.         this.saturation = saturation;
  516.         this.brightness = brightness;
  517.     target.setColor(0xFF0000);
  518.     enableEvents(AWTEvent.MOUSE_EVENT_MASK
  519.              | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  520.     }
  521.     protected void locateImage() {
  522.     if (img == null) {
  523.         final Dimension s = getSize();
  524.         final int c0 = color;
  525.             final Adjustable imageHue = hue;
  526.         hli = new SyntheticImage() {
  527.         int color, nextColor;
  528.                 Adjustable hue;
  529.         boolean changed;
  530.         {
  531.                     this.hue = imageHue;
  532.             this.width = s.width;
  533.             this.height = s.height;
  534.             nextFrame(c0);
  535.         }
  536.         public synchronized void nextFrame(int c) {
  537.             c |= pixMask << 24;
  538.             if (color != c) {
  539.             changed = true;
  540.             notifyAll();
  541.             }
  542.             nextColor = c;
  543.         }
  544.         private synchronized void waitNextColor() {
  545.             try {
  546.             while (!changed)
  547.                 wait();
  548.             }
  549.             catch(InterruptedException e) {
  550.             }
  551.             changed = false;
  552.             color = nextColor;
  553.         }
  554.         protected boolean isStatic() {
  555.             return false;
  556.         }
  557.         protected void computeRow(int y, int[] row) {
  558.             if (y == 0)
  559.             waitNextColor();
  560.             float theta = hue.getValue() / ((float) 360.0);
  561.             float brightness = (height - y - 1) / (float) (height - 1);
  562.             int w = width;
  563.             for (int i = w; --i >= 0;) {
  564.             float sat = (w - i - 1) / (float) (w - 1);
  565.             row[i] = Color.HSBtoRGB(theta, sat, brightness);
  566.             }
  567.         }
  568.         };
  569.         setImage(getToolkit().createImage(hli));
  570.     }
  571.     }
  572.     protected void processMouseEvent(MouseEvent e) {
  573.     switch (e.getID()) {
  574.       case MouseEvent.MOUSE_PRESSED:
  575.       case MouseEvent.MOUSE_RELEASED:
  576.       case MouseEvent.MOUSE_DRAGGED:
  577.         int h = isize.height;
  578.         int w = isize.width;
  579.         brightness.setValue((h - e.getY() - 1) * 100 / (h - 1));
  580.         saturation.setValue((w - e.getX() - 1) * 100 / (w - 1));
  581.     }
  582.     }
  583.     public void setColor(int c) {
  584.     color = c;
  585.     if (hli != null)
  586.         hli.nextFrame(c);
  587.     }
  588.     protected void processMouseMotionEvent(MouseEvent e) {
  589.     processMouseEvent(e);
  590.     }
  591.     public void adjustmentValueChanged(AdjustmentEvent e) {
  592.     int h = isize.height - 1;
  593.     int w = isize.width - 1;
  594.     int tx = w - saturation.getValue() * w / 100;
  595.     int ty = h - brightness.getValue() * h / 100;
  596.     Graphics g = getGraphics();
  597.     if (g != null) {
  598.         paintCursor(g, sx, sy);
  599.         paintCursor(g, tx, ty);
  600.         g.dispose();
  601.     }
  602.     sx = tx;
  603.     sy = ty;
  604.     propogateColor();
  605.     }
  606.     void propogateColor() {
  607.     int h = isize.height;
  608.     int w = isize.width;
  609.     target.setColor(Color.HSBtoRGB(hue.getValue() / ((float) 360.0),
  610.                  saturation.getValue() / ((float) 100.0),
  611.                brightness.getValue() / ((float) 100.0)));
  612.     }
  613.     public void paint(Graphics g) {
  614.     super.paint(g);
  615.     paintCursor(g, sx, sy);
  616.     }
  617.     private void paintCursor(Graphics g, int sx, int sy) {
  618.     g.setColor(Color.white);
  619.     g.setXORMode(Color.black);
  620.     g.drawLine(sx - 3, sy, sx + 3, sy);
  621.     g.drawLine(sx, sy - 3, sx, sy + 3);
  622.     }
  623.     public void update(Graphics g) {
  624.     paint(g);
  625.     }
  626. }
  627.  
  628.     class ColorPatch extends ImageComponent {
  629.         SyntheticImage vSrc;
  630.         int patchColor;
  631.         protected void locateImage() {
  632.         if (img == null) {
  633.             final Dimension s = getSize();
  634.         final int c0 = patchColor;
  635.         vSrc = new SyntheticImage() {
  636.             int color, nextColor;
  637.             boolean changed;
  638.             {
  639.                 this.width = s.width;
  640.             this.height = s.height;
  641.             nextFrame(c0);
  642.             }
  643.             public synchronized void nextFrame(int c) {
  644.                 c |= pixMask << 24;
  645.             if (color != c) {
  646.                 changed = true;
  647.                 notifyAll();
  648.             }
  649.             nextColor = c;
  650.             }
  651.             private synchronized void waitNextColor() {
  652.                 try {
  653.                 while (!changed)
  654.                     wait();
  655.             }
  656.             catch(InterruptedException e) {
  657.             }
  658.             changed = false;
  659.             color = nextColor;
  660.             }
  661.             protected boolean isStatic() {
  662.                 return false;
  663.             }
  664.             protected void computeRow(int y, int[] row) {
  665.                 if (y == 0)
  666.                 waitNextColor();
  667.             int C = color;
  668.             for (int i = row.length; --i >= 0;)
  669.                 row[i] = C;
  670.             }
  671.         };
  672.         setImage(getToolkit().createImage(vSrc));
  673.         }
  674.     }
  675.         public void setColor(int c) {
  676.         int old = patchColor;
  677.         patchColor = c;
  678.         if (vSrc != null)
  679.             vSrc.nextFrame(c);
  680.         if (old != c) {
  681.         firePropertyChange( "color", new Color( old ), new Color( patchColor ) );
  682.         }
  683.     }
  684.     }
  685.