001    /*
002     * $Id: RadialGradientPainter.java,v 1.3 2006/03/26 07:02:08 rbair Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx.painter.gradient;
023    
024    import java.awt.Paint;
025    import java.awt.geom.Point2D;
026    import org.apache.batik.ext.awt.RadialGradientPaint;
027    import org.jdesktop.swingx.util.Resize;
028    
029    /**
030     * <p>A Gradient based painter used for painting "multi-stop" radial gradients. These are
031     * gradients that imploys more than 2 colors, where each color is defined along
032     * with a float value between 0 and 1 indicating at what point along the gradient
033     * the new color is used.</p>
034     *
035     * <p>As with BasicGradienPainter and mentioned in AbstractGradientPainter, the values
036     * given to the centerPoint, radius, and focusPoint of the RadialGradientPainter are crucial. They
037     * represent what distance from the origin the gradient should begin and end at,
038     * depending on the size of the component. That is, they must be specified as values between
039     * 0 and 1, where 0 means "all the way on the left/top" and 1 means "all the way on the
040     * right/bottom".</p>
041     *
042     * <p>In addition, the resize behavior of the radius is specified in the resizeRadius
043     * property. If HORIZONTAL, then the width of the component is used to calculate
044     * the new radius. If VERTICAL then the height of the component is used. If BOTH,
045     * then the Math.min(width, height) is used. If NONE, then no resize occurs for
046     * the radius.</p>
047     *
048     * <p><strong>NOTE: RadialGradientPainter relies on LinearGradientPaint, which is
049     * included in the optional jar MultipleGradientPaint.jar. Be sure to have this
050     * jar on your classpath if you use this class</strong></p>
051     *
052     * @author rbair
053     */
054    public class RadialGradientPainter extends AbstractGradientPainter {
055        private RadialGradientPaint paint;
056        private Resize resizeRadius = Resize.BOTH;
057        
058        /** Creates a new instance of RadialGradientPainter */
059        public RadialGradientPainter() {
060        }
061        
062        /** 
063         * Creates a new instance of RadialGradientPainter 
064         * with the given RadialGradientPaint
065         *
066         * @param paint the RadialGradientPaint to use
067         */
068        public RadialGradientPainter(RadialGradientPaint paint) {
069            this.paint = paint;
070        }
071        
072        /**
073         * Set the gradient paint to use. This may be null. If null, nothing is painted
074         *
075         * @param paint the RadialGradientPaint to use
076         */
077        public void setGradientPaint(RadialGradientPaint paint) {
078            RadialGradientPaint old = getGradientPaint();
079            this.paint = paint;
080            firePropertyChange("gradientPaint", old, getGradientPaint());
081        }
082        
083        /**
084         * @return the RadialGradientPaint used for painting. This may be null
085         */
086        public RadialGradientPaint getGradientPaint() {
087            return paint;
088        }
089        
090        /**
091         * Specifies the resize behavior for the radius of the RadialGradientPaint.
092         * If HORIZONTAL, then the width of the component is used to calculate
093         * the new radius. If VERTICAL then the height of the component is used. If BOTH,
094         * then the Math.min(width, height) is used. If NONE, then no resize occurs for
095         * the radius.
096         *
097         * @param r the Resize behavior for the radius
098         */
099        public void setResizeRadius(Resize r) {
100            Resize old = getResizeRadius();
101            this.resizeRadius = r;
102            firePropertyChange("resizeRadius", old, getResizeRadius());
103        }
104        
105        /**
106         * @return the resize behavior for the radius
107         */
108        public Resize getResizeRadius() {
109            return resizeRadius;
110        }
111        
112        /**
113         * @inheritDoc
114         */
115        protected Paint calculateSizedPaint(int width, int height) {
116            RadialGradientPaint paint = getGradientPaint();
117            if (paint == null) {
118                return null;
119            }
120            
121            Point2D centerPoint = paint.getCenterPoint();
122            Point2D focusPoint = paint.getFocusPoint();
123            
124            double x1 = isResizeHorizontal() ? centerPoint.getX() * width : centerPoint.getX();
125            double y1 = isResizeVertical() ? centerPoint.getY() * height : centerPoint.getY();
126            double x2 = isResizeHorizontal() ? focusPoint.getX() * width : focusPoint.getX();
127            double y2 = isResizeVertical() ? focusPoint.getY() * height : focusPoint.getY();
128            centerPoint = new Point2D.Double(x1, y1);
129            focusPoint = new Point2D.Double(x2, y2);
130    
131            float radius = paint.getRadius();
132            Resize r = getResizeRadius();
133            r = r == null ? Resize.BOTH : r;
134            switch (r) {
135                case HORIZONTAL:
136                    radius = radius * width;
137                    break;
138                case VERTICAL:
139                    radius = radius * height;
140                    break;
141                case BOTH:
142                    radius = radius * Math.min(width, height);
143                    break;
144                case NONE:
145                    break;
146                default:
147                    throw new AssertionError("Cannot happen");
148            }
149            
150            return new RadialGradientPaint(
151                    centerPoint,
152                    radius,
153                    focusPoint,
154                    paint.getFractions(),
155                    paint.getColors(),
156                    paint.getCycleMethod(),
157                    paint.getColorSpace(),
158                    paint.getTransform());
159        }
160    }