View Javadoc
1   /*
2    * @(#)PlafPaintUtils.java
3    *
4    * $Date: 2014-12-30 21:41:58 -0500 (Tue, 30 Dec 2014) $
5    *
6    * Copyright (c) 2011 by Jeremy Wood.
7    * All rights reserved.
8    *
9    * The copyright of this software is owned by Jeremy Wood. 
10   * You may not use, copy or modify this software, except in  
11   * accordance with the license agreement you entered into with  
12   * Jeremy Wood. For details see accompanying license terms.
13   * 
14   * This software is probably, but not necessarily, discussed here:
15   * https://javagraphics.java.net/
16   * 
17   * That site should also contain the most recent official version
18   * of this software.  (See the SVN repository for more details.)
19   */
20  package com.bric.multislider;
21  
22  import java.awt.BasicStroke;
23  import java.awt.Color;
24  import java.awt.Graphics;
25  import java.awt.Graphics2D;
26  import java.awt.Paint;
27  import java.awt.Rectangle;
28  import java.awt.RenderingHints;
29  import java.awt.Shape;
30  import java.awt.TexturePaint;
31  import java.awt.image.BufferedImage;
32  import java.util.Hashtable;
33  
34  import javax.swing.SwingConstants;
35  import javax.swing.UIManager;
36  
37  /**
38   * Some static methods for some common painting functions.
39   * @author Jeremy Wood
40   **/
41  public class PlafPaintUtils
42  {
43  
44      /** Four shades of white, each with increasing opacity. */
45      final static Color[] whites = new Color[]{new Color(255, 255, 255, 50), new Color(255, 255, 255, 100),
46              new Color(255, 255, 255, 150)};
47  
48      /** Four shades of black, each with increasing opacity. */
49      final static Color[] blacks = new Color[]{new Color(0, 0, 0, 50), new Color(0, 0, 0, 100), new Color(0, 0, 0, 150)};
50  
51      /**
52       * @return the color used to indicate when a component has focus. By default this uses the color (64,113,167), but
53       *         you can override this by calling: <BR>
54       *         <code>UIManager.put("focusRing",customColor);</code>
55       */
56      public static Color getFocusRingColor()
57      {
58          Object obj = UIManager.getColor("Focus.color");
59          if (obj instanceof Color)
60              return (Color) obj;
61          obj = UIManager.getColor("focusRing");
62          if (obj instanceof Color)
63              return (Color) obj;
64          return new Color(64, 113, 167);
65      }
66  
67      /**
68       * Paints 3 different strokes around a shape to indicate focus. The widest stroke is the most transparent, so this
69       * achieves a nice "glow" effect.
70       * <P>
71       * The catch is that you have to render this underneath the shape, and the shape should be filled completely.
72       * @param g the graphics to paint to
73       * @param shape the shape to outline
74       * @param pixelSize the number of pixels the outline should cover.
75       */
76      public static void paintFocus(Graphics2D g, Shape shape, int pixelSize)
77      {
78          paintFocus(g, shape, pixelSize, getFocusRingColor(), true);
79      }
80  
81      /**
82       * Paints 3 different strokes around a shape to indicate focus. The widest stroke is the most transparent, so this
83       * achieves a nice "glow" effect.
84       * <P>
85       * The catch is that you have to render this underneath the shape, and the shape should be filled completely.
86       * @param g the graphics to paint to
87       * @param shape the shape to outline
88       * @param pixelSize the number of pixels the outline should cover.
89       * @param focusColor the color of the focus ring to paint
90       * @param changeRenderingHints if true then the rendering hints will be modified, if false they will be left in tact
91       */
92      public static void paintFocus(Graphics2D g, Shape shape, int pixelSize, Color focusColor,
93              boolean changeRenderingHints)
94      {
95          g = (Graphics2D) g.create();
96          try
97          {
98              Color[] focusArray =
99                      new Color[]{
100                             new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
101                                     235 * focusColor.getAlpha() / 255),
102                             new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
103                                     130 * focusColor.getAlpha() / 255),
104                             new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
105                                     80 * focusColor.getAlpha() / 255)};
106             if (changeRenderingHints)
107             {
108                 if (JVM.usingQuartz)
109                 {
110                     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
111                     g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
112                 }
113                 else
114                 {
115                     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
116                     g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
117                 }
118             }
119 
120             g.setStroke(new BasicStroke(2 * pixelSize + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
121             g.setColor(focusArray[2]);
122             g.draw(shape);
123             if (2 * pixelSize + 1 > 0)
124             {
125                 g.setStroke(new BasicStroke(2 * pixelSize - 2 + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
126                 g.setColor(focusArray[1]);
127                 g.draw(shape);
128             }
129             if (2 * pixelSize - 4 + 1 > 0)
130             {
131                 g.setStroke(new BasicStroke(2 * pixelSize - 4 + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
132                 g.setColor(focusArray[0]);
133                 g.draw(shape);
134             }
135         }
136         finally
137         {
138             g.dispose();
139         }
140     }
141 
142     /**
143      * Uses translucent shades of white and black to draw highlights and shadows around a rectangle, and then frames the
144      * rectangle with a shade of gray (120).
145      * <P>
146      * This should be called to add a finishing touch on top of existing graphics.
147      * @param g the graphics to paint to.
148      * @param r the rectangle to paint.
149      */
150     public static void drawBevel(Graphics2D g, Rectangle r)
151     {
152         g.setStroke(new BasicStroke(1));
153         drawColors(blacks, g, r.x, r.y + r.height, r.x + r.width, r.y + r.height, SwingConstants.SOUTH);
154         drawColors(blacks, g, r.x + r.width, r.y, r.x + r.width, r.y + r.height, SwingConstants.EAST);
155 
156         drawColors(whites, g, r.x, r.y, r.x + r.width, r.y, SwingConstants.NORTH);
157         drawColors(whites, g, r.x, r.y, r.x, r.y + r.height, SwingConstants.WEST);
158 
159         g.setColor(new Color(120, 120, 120));
160         g.drawRect(r.x, r.y, r.width, r.height);
161     }
162 
163     private static void drawColors(Color[] colors, Graphics g, int x1, int y1, int x2, int y2, int direction)
164     {
165         for (int a = 0; a < colors.length; a++)
166         {
167             g.setColor(colors[colors.length - a - 1]);
168             if (direction == SwingConstants.SOUTH)
169             {
170                 g.drawLine(x1, y1 - a, x2, y2 - a);
171             }
172             else if (direction == SwingConstants.NORTH)
173             {
174                 g.drawLine(x1, y1 + a, x2, y2 + a);
175             }
176             else if (direction == SwingConstants.EAST)
177             {
178                 g.drawLine(x1 - a, y1, x2 - a, y2);
179             }
180             else if (direction == SwingConstants.WEST)
181             {
182                 g.drawLine(x1 + a, y1, x2 + a, y2);
183             }
184         }
185     }
186 
187     /** The table used to store vertical gradients. */
188     private static Hashtable<String, TexturePaint> verticalGradients;
189 
190     /**
191      * Create a vertical gradient. This gradient is stored in a table and reused throughout the rest of this session.
192      * @param name an identifying key for this gradient (used to cache it).
193      * @param height the height of the gradient
194      * @param y the y offset of the gradient
195      * @param positions the fractional positions of each color (between [0,1]).
196      * @param colors one color for each position.
197      * @return the vertical gradient.
198      */
199     synchronized static Paint getVerticalGradient(String name, int height, int y, float[] positions, Color[] colors)
200     {
201         if (verticalGradients == null)
202         {
203             verticalGradients = new Hashtable<String, TexturePaint>();
204         }
205 
206         String key = name + " " + height + " " + y;
207         TexturePaint paint = verticalGradients.get(key);
208         if (paint == null)
209         {
210             height = Math.max(height, 1); // before a component is laid out, it may be 0x0
211             BufferedImage bi = new BufferedImage(1, height, BufferedImage.TYPE_INT_ARGB);
212             int[] array = new int[height];
213             for (int a = 0; a < array.length; a++)
214             {
215                 float f = a;
216                 f = f / ((array.length - 1));
217                 boolean hit = false;
218                 findMatch: for (int b = 1; b < positions.length; b++)
219                 {
220                     if (f >= positions[b - 1] && f < positions[b])
221                     {
222                         float p = (f - positions[b - 1]) / (positions[b] - positions[b - 1]);
223                         array[a] = tween(colors[b - 1], colors[b], p).getRGB();
224                         hit = true;
225                         break findMatch;
226                     }
227                 }
228                 if (!hit)
229                     array[a] = colors[colors.length - 1].getRGB();
230             }
231             bi.getRaster().setDataElements(0, 0, 1, height, array);
232             paint = new TexturePaint(bi, new Rectangle(0, y, 1, height));
233             verticalGradients.put(key, paint);
234         }
235         return paint;
236     }
237 
238     /** 
239      * Tweens between the two arguments.
240      * @param c1 color 1
241      * @param c2 color 2
242      * @param p if 0, r1; if 1, r2; value between 0 and 1 makes it closer to r1 or r2.
243      * @return tweened color
244      */
245     private static Color tween(Color c1, Color c2, float p)
246     {
247         int r1 = c1.getRed();
248         int g1 = c1.getGreen();
249         int b1 = c1.getBlue();
250         int a1 = c1.getAlpha();
251 
252         int r2 = c2.getRed();
253         int g2 = c2.getGreen();
254         int b2 = c2.getBlue();
255         int a2 = c2.getAlpha();
256 
257         return new Color((int) (r1 * (1 - p) + r2 * p), (int) (g1 * (1 - p) + g2 * p), (int) (b1 * (1 - p) + b2 * p),
258                 (int) (a1 * (1 - p) + a2 * p));
259     }
260 
261     private static Hashtable<String, TexturePaint> checkers;
262 
263     public static TexturePaint getCheckerBoard(int checkerSize)
264     {
265         return getCheckerBoard(checkerSize, Color.white, Color.lightGray);
266     }
267 
268     public static TexturePaint getCheckerBoard(int checkerSize, Color color1, Color color2)
269     {
270         String key = checkerSize + " " + color1.toString() + " " + color2.toString();
271         if (checkers == null)
272             checkers = new Hashtable<String, TexturePaint>();
273         TexturePaint paint = checkers.get(key);
274         if (paint == null)
275         {
276             BufferedImage bi = new BufferedImage(2 * checkerSize, 2 * checkerSize, BufferedImage.TYPE_INT_RGB);
277             Graphics2D g = bi.createGraphics();
278             g.setColor(color1);
279             g.fillRect(0, 0, 2 * checkerSize, 2 * checkerSize);
280             g.setColor(color2);
281             g.fillRect(0, 0, checkerSize, checkerSize);
282             g.fillRect(checkerSize, checkerSize, checkerSize, checkerSize);
283             g.dispose();
284             paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
285             checkers.put(key, paint);
286         }
287         return paint;
288     }
289 }