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 =
46              new Color[] {new Color(255, 255, 255, 50), new Color(255, 255, 255, 100), 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 you can
53       *         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 achieves
69       * 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 achieves
83       * 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, boolean changeRenderingHints)
93      {
94          g = (Graphics2D) g.create();
95          try
96          {
97              Color[] focusArray = new Color[] {
98                      new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
99                              235 * focusColor.getAlpha() / 255),
100                     new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
101                             130 * focusColor.getAlpha() / 255),
102                     new Color(focusColor.getRed(), focusColor.getGreen(), focusColor.getBlue(),
103                             80 * focusColor.getAlpha() / 255)};
104             if (changeRenderingHints)
105             {
106                 if (Jvm.usingQuartz)
107                 {
108                     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
109                     g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
110                 }
111                 else
112                 {
113                     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
114                     g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
115                 }
116             }
117 
118             g.setStroke(new BasicStroke(2 * pixelSize + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
119             g.setColor(focusArray[2]);
120             g.draw(shape);
121             if (2 * pixelSize + 1 > 0)
122             {
123                 g.setStroke(new BasicStroke(2 * pixelSize - 2 + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
124                 g.setColor(focusArray[1]);
125                 g.draw(shape);
126             }
127             if (2 * pixelSize - 4 + 1 > 0)
128             {
129                 g.setStroke(new BasicStroke(2 * pixelSize - 4 + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
130                 g.setColor(focusArray[0]);
131                 g.draw(shape);
132             }
133         }
134         finally
135         {
136             g.dispose();
137         }
138     }
139 
140     /**
141      * Uses translucent shades of white and black to draw highlights and shadows around a rectangle, and then frames the
142      * rectangle with a shade of gray (120).
143      * <P>
144      * This should be called to add a finishing touch on top of existing graphics.
145      * @param g the graphics to paint to.
146      * @param r the rectangle to paint.
147      */
148     public static void drawBevel(Graphics2D g, Rectangle r)
149     {
150         g.setStroke(new BasicStroke(1));
151         drawColors(blacks, g, r.x, r.y + r.height, r.x + r.width, r.y + r.height, SwingConstants.SOUTH);
152         drawColors(blacks, g, r.x + r.width, r.y, r.x + r.width, r.y + r.height, SwingConstants.EAST);
153 
154         drawColors(whites, g, r.x, r.y, r.x + r.width, r.y, SwingConstants.NORTH);
155         drawColors(whites, g, r.x, r.y, r.x, r.y + r.height, SwingConstants.WEST);
156 
157         g.setColor(new Color(120, 120, 120));
158         g.drawRect(r.x, r.y, r.width, r.height);
159     }
160 
161     private static void drawColors(Color[] colors, Graphics g, int x1, int y1, int x2, int y2, int direction)
162     {
163         for (int a = 0; a < colors.length; a++)
164         {
165             g.setColor(colors[colors.length - a - 1]);
166             if (direction == SwingConstants.SOUTH)
167             {
168                 g.drawLine(x1, y1 - a, x2, y2 - a);
169             }
170             else if (direction == SwingConstants.NORTH)
171             {
172                 g.drawLine(x1, y1 + a, x2, y2 + a);
173             }
174             else if (direction == SwingConstants.EAST)
175             {
176                 g.drawLine(x1 - a, y1, x2 - a, y2);
177             }
178             else if (direction == SwingConstants.WEST)
179             {
180                 g.drawLine(x1 + a, y1, x2 + a, y2);
181             }
182         }
183     }
184 
185     /** The table used to store vertical gradients. */
186     private static Hashtable<String, TexturePaint> verticalGradients;
187 
188     /**
189      * Create a vertical gradient. This gradient is stored in a table and reused throughout the rest of this session.
190      * @param name an identifying key for this gradient (used to cache it).
191      * @param height the height of the gradient
192      * @param y the y offset of the gradient
193      * @param positions the fractional positions of each color (between [0,1]).
194      * @param colors one color for each position.
195      * @return the vertical gradient.
196      */
197     synchronized static Paint getVerticalGradient(String name, int height, int y, float[] positions, Color[] colors)
198     {
199         if (verticalGradients == null)
200         {
201             verticalGradients = new Hashtable<String, TexturePaint>();
202         }
203 
204         String key = name + " " + height + " " + y;
205         TexturePaint paint = verticalGradients.get(key);
206         if (paint == null)
207         {
208             height = Math.max(height, 1); // before a component is laid out, it may be 0x0
209             BufferedImage bi = new BufferedImage(1, height, BufferedImage.TYPE_INT_ARGB);
210             int[] array = new int[height];
211             for (int a = 0; a < array.length; a++)
212             {
213                 float f = a;
214                 f = f / ((array.length - 1));
215                 boolean hit = false;
216                 findMatch: for (int b = 1; b < positions.length; b++)
217                 {
218                     if (f >= positions[b - 1] && f < positions[b])
219                     {
220                         float p = (f - positions[b - 1]) / (positions[b] - positions[b - 1]);
221                         array[a] = tween(colors[b - 1], colors[b], p).getRGB();
222                         hit = true;
223                         break findMatch;
224                     }
225                 }
226                 if (!hit)
227                     array[a] = colors[colors.length - 1].getRGB();
228             }
229             bi.getRaster().setDataElements(0, 0, 1, height, array);
230             paint = new TexturePaint(bi, new Rectangle(0, y, 1, height));
231             verticalGradients.put(key, paint);
232         }
233         return paint;
234     }
235 
236     /**
237      * Tweens between the two arguments.
238      * @param c1 color 1
239      * @param c2 color 2
240      * @param p if 0, r1; if 1, r2; value between 0 and 1 makes it closer to r1 or r2.
241      * @return tweened color
242      */
243     private static Color tween(Color c1, Color c2, float p)
244     {
245         int r1 = c1.getRed();
246         int g1 = c1.getGreen();
247         int b1 = c1.getBlue();
248         int a1 = c1.getAlpha();
249 
250         int r2 = c2.getRed();
251         int g2 = c2.getGreen();
252         int b2 = c2.getBlue();
253         int a2 = c2.getAlpha();
254 
255         return new Color((int) (r1 * (1 - p) + r2 * p), (int) (g1 * (1 - p) + g2 * p), (int) (b1 * (1 - p) + b2 * p),
256                 (int) (a1 * (1 - p) + a2 * p));
257     }
258 
259     private static Hashtable<String, TexturePaint> checkers;
260 
261     public static TexturePaint getCheckerBoard(int checkerSize)
262     {
263         return getCheckerBoard(checkerSize, Color.white, Color.lightGray);
264     }
265 
266     public static TexturePaint getCheckerBoard(int checkerSize, Color color1, Color color2)
267     {
268         String key = checkerSize + " " + color1.toString() + " " + color2.toString();
269         if (checkers == null)
270             checkers = new Hashtable<String, TexturePaint>();
271         TexturePaint paint = checkers.get(key);
272         if (paint == null)
273         {
274             BufferedImage bi = new BufferedImage(2 * checkerSize, 2 * checkerSize, BufferedImage.TYPE_INT_RGB);
275             Graphics2D g = bi.createGraphics();
276             g.setColor(color1);
277             g.fillRect(0, 0, 2 * checkerSize, 2 * checkerSize);
278             g.setColor(color2);
279             g.fillRect(0, 0, checkerSize, checkerSize);
280             g.fillRect(checkerSize, checkerSize, checkerSize, checkerSize);
281             g.dispose();
282             paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
283             checkers.put(key, paint);
284         }
285         return paint;
286     }
287 }