001/*
002 * $Id: NeonBorderEffect.java 3829 2010-10-07 02:01:20Z kschaefe $
003 *
004 * Copyright 2006 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
023package org.jdesktop.swingx.painter.effects;
024
025import java.awt.AlphaComposite;
026import java.awt.BasicStroke;
027import java.awt.Color;
028import java.awt.Graphics2D;
029import java.awt.Point;
030import java.awt.Shape;
031
032/**
033 * An effect which draws a multicolored border around a painter's shape.
034 * It will interpolate between two specified colors, creating a neon like effect.
035 * @author joshy
036 */
037public class NeonBorderEffect extends AbstractAreaEffect {
038    
039    private Color edgeColor;
040    private Color centerColor;
041    private BorderPosition borderPosition = BorderPosition.Outside;
042    
043    /**
044     * An enum representing the position of the border: inside, outside, or centered on the border.
045     */
046    public enum BorderPosition { Inside, Centered, Outside }
047    
048    
049    /**
050     * Create a new NeonBorderEffect
051     */
052    public NeonBorderEffect() {
053        this(Color.GREEN, Color.WHITE, 10);
054    }
055    
056    /** Creates a new instance of NeonBorderEffect */
057    public NeonBorderEffect(Color edgeColor, Color centerColor, int effectWidth) {
058        super();
059        setEffectWidth(effectWidth);
060        this.setEdgeColor(edgeColor);
061        this.setCenterColor(centerColor);
062        this.setRenderInsideShape(false);
063        this.setShouldFillShape(false);
064        this.setOffset(new Point(0,0));
065    }
066    
067    @Override
068    protected void paintBorderGlow(Graphics2D gfx, Shape clipShape, int width, int height) {
069        
070        /*
071        // draw the effect
072        for(float i=steps-1; i>=0; i=i-1f) {
073            float brushWidth = i * getEffectWidth()/steps;
074            gfx.setPaint(interpolateColor(i/steps,edgeColor,centerColor));
075            gfx.setStroke(new BasicStroke(brushWidth,
076                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
077            gfx.draw(clipShape);
078        }*/
079        
080        /* // an interesting outline effect. stroke the shape with a wide brush
081         * // then stroke again with slightly less wide one, then don't fill the middle
082        for(int i=0; i<2; i++) {
083            float brushWidth = (2-i)*5;
084            p("widdth = " + brushWidth);
085            gfx.setPaint(interpolateColor((float)(1-i), Color.BLACK, Color.WHITE));
086            gfx.setStroke(new BasicStroke(brushWidth));
087            gfx.draw(clipShape);
088        }
089         */
090        gfx.translate(getOffset().getX(), getOffset().getY());
091        gfx.setComposite(AlphaComposite.SrcOver);
092        int steps = getEffectWidth();
093        if(borderPosition == BorderPosition.Centered) {
094            steps = steps/2;
095        }
096        for(int i=0; i<steps; i++) {
097            
098            // make the brush width smaller each time until there is nothing left
099            float brushWidth = steps+1-i;
100            float half = steps/2;
101            
102            if(borderPosition == BorderPosition.Centered) {
103                gfx.setPaint(interpolateColor((float)(steps-i)/steps, getEdgeColor(), getCenterColor()));
104            } else {
105                if(i<half) {
106                    gfx.setPaint(interpolateColor((half-i)/half, getEdgeColor(), getCenterColor()));
107                } else {
108                    gfx.setPaint(interpolateColor((i-half)/half, getEdgeColor(), getCenterColor()));
109                }
110            }
111            
112            // to make the effect softer use a different stroke
113            gfx.setStroke(new BasicStroke(brushWidth,
114                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
115            //gfx.setStroke(new BasicStroke(brushWidth));
116            gfx.draw(clipShape);
117        }
118        gfx.translate(-getOffset().getX(), -getOffset().getY());
119        
120    }
121    
122    protected Color interpolateColor(float t, Color start, Color end) {
123        float[] partsS = start.getRGBComponents(null);
124        float[] partsE = end.getRGBComponents(null);
125        float[] partsR = new float[4];
126        for(int i=0; i<4; i++) {
127            partsR[i] = (partsS[i] - partsE[i])*t + partsE[i];
128        }
129        return new Color(partsR[0],partsR[1],partsR[2],partsR[3]);
130    }
131    
132    /**
133     * Gets the current edge color.
134     * @return current edge color
135     */
136    public Color getEdgeColor() {
137        return edgeColor;
138    }
139    
140    /**
141     * Set the edge color
142     * @param edgeColor 
143     */
144    public void setEdgeColor(Color edgeColor) {
145        this.edgeColor = edgeColor;
146    }
147    
148    /**
149     * 
150     * @return color in the center of the effect
151     */
152    public Color getCenterColor() {
153        return centerColor;
154    }
155    
156    /**
157     * 
158     * @param centerColor color in the center of the effect.
159     * @see #getCenterColor()
160     */
161    public void setCenterColor(Color centerColor) {
162        this.centerColor = centerColor;
163    }
164    
165    /**
166     * 
167     * @return position of the border relative to the edge of painter covered area.
168     * @see BorderPosition
169     */
170    public BorderPosition getBorderPosition() {
171        return borderPosition;
172    }
173    
174    /**
175     * 
176     * @param borderPosition position of the border relative to the edge of painter covered area.
177     * @see #getBorderPosition()
178     * @see BorderPosition
179     */
180    public void setBorderPosition(BorderPosition borderPosition) {
181        this.borderPosition = borderPosition;
182        switch(borderPosition) {
183            case Centered : 
184                setShapeMasked(false);
185                break;
186            case Inside :
187                setShapeMasked(true);
188                setRenderInsideShape(true);
189                break;
190            case Outside :
191                setShapeMasked(true);
192                setRenderInsideShape(false);
193                break;
194        }
195        if(borderPosition == BorderPosition.Centered) {
196        }
197    }
198}