1 /*
2 * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 
24 package com.sun.swingset3.demos.colorchooser;
25 
26 import java.awt.*;
27 import java.awt.event.HierarchyEvent;
28 import java.awt.event.HierarchyListener;
29 import java.awt.geom.GeneralPath;
30 import java.awt.image.BufferedImage;
31 import java.lang.reflect.InvocationTargetException;
32 import java.util.Hashtable;
33 import java.util.Map;
34 import javax.swing.*;
35 
36 import static com.sun.swingset3.demos.colorchooser.BezierAnimationPanel.BezierColor.*;
37 
38 /**
39  * BezierAnimationPanel
40  *
41  * @author Jim Graham
42  * @author Jeff Dinkins (removed dynamic setting changes, made swing friendly)
43  * @version 1.16 11/17/05
44  */
45 public class BezierAnimationPanel extends JPanel implements Runnable {
46     public static enum BezierColor {
47         BACKGROUND, OUTER, GRADIENT_A, GRADIENT_B
48     }
49 
50     private final Map<BezierColor, Color> colors = new Hashtable<BezierColor, Color>();
51 
52     private GradientPaint gradient = null;
53 
54     private static final int NUMPTS = 6;
55 
56     private final float[] animpts = new float[NUMPTS * 2];
57 
58     private final float[] deltas = new float[NUMPTS * 2];
59 
60     private BufferedImage img;
61 
62     private Thread anim;
63 
64     private final Object lock = new Object();
65 
66     /**
67      * BezierAnimationPanel Constructor
68      */
BezierAnimationPanel()69     public BezierAnimationPanel() {
70         setOpaque(true);
71 
72         colors.put(BACKGROUND, new Color(0, 0, 153));
73         colors.put(OUTER, new Color(255, 255, 255));
74         colors.put(GRADIENT_A, new Color(255, 0, 101));
75         colors.put(GRADIENT_B, new Color(255, 255, 0));
76 
77         addHierarchyListener(new HierarchyListener() {
78             public void hierarchyChanged(HierarchyEvent e) {
79                 if (isShowing()) {
80                     start();
81                 } else {
82                     stop();
83                 }
84             }
85         });
86     }
87 
getBezierColor(BezierColor bezierColor)88     public Color getBezierColor(BezierColor bezierColor) {
89         return colors.get(bezierColor);
90     }
91 
setBezierColor(BezierColor bezierColor, Color value)92     public void setBezierColor(BezierColor bezierColor, Color value) {
93         if (value != null) {
94             colors.put(bezierColor, value);
95         }
96     }
97 
start()98     public void start() {
99         Dimension size = getSize();
100         for (int i = 0; i < animpts.length; i += 2) {
101             animpts[i] = (float) (Math.random() * size.width);
102             animpts[i + 1] = (float) (Math.random() * size.height);
103             deltas[i] = (float) (Math.random() * 4.0 + 2.0);
104             deltas[i + 1] = (float) (Math.random() * 4.0 + 2.0);
105             if (animpts[i] > size.width / 6.0f) {
106                 deltas[i] = -deltas[i];
107             }
108             if (animpts[i + 1] > size.height / 6.0f) {
109                 deltas[i + 1] = -deltas[i + 1];
110             }
111         }
112         anim = new Thread(this);
113         anim.setPriority(Thread.MIN_PRIORITY);
114         anim.start();
115     }
116 
stop()117     public synchronized void stop() {
118         anim = null;
119         notify();
120     }
121 
animate(float[] pts, float[] deltas, int index, int limit)122     private static void animate(float[] pts, float[] deltas, int index, int limit) {
123         float newpt = pts[index] + deltas[index];
124         if (newpt <= 0) {
125             newpt = -newpt;
126             deltas[index] = (float) (Math.random() * 3.0 + 2.0);
127         } else if (newpt >= (float) limit) {
128             newpt = 2.0f * limit - newpt;
129             deltas[index] = -(float) (Math.random() * 3.0 + 2.0);
130         }
131         pts[index] = newpt;
132     }
133 
run()134     public void run() {
135         Thread me = Thread.currentThread();
136         while (getSize().width <= 0) {
137             try {
138                 Thread.sleep(500);
139             } catch (InterruptedException e) {
140                 return;
141             }
142         }
143 
144         Graphics2D g2d = null;
145         Graphics2D bufferG2D = null;
146         BasicStroke solid = new BasicStroke(9.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 9.0f);
147         GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
148         int rule = AlphaComposite.SRC_OVER;
149         AlphaComposite opaque = AlphaComposite.SrcOver;
150         AlphaComposite blend = AlphaComposite.getInstance(rule, 0.9f);
151         AlphaComposite set = AlphaComposite.Src;
152         Dimension oldSize = getSize();
153         Shape clippath = null;
154         while (anim == me) {
155             Dimension size = getSize();
156             if (size.width != oldSize.width || size.height != oldSize.height) {
157                 img = null;
158                 clippath = null;
159                 if (bufferG2D != null) {
160                     bufferG2D.dispose();
161                     bufferG2D = null;
162                 }
163             }
164             oldSize = size;
165 
166             if (img == null) {
167                 img = (BufferedImage) createImage(size.width, size.height);
168             }
169 
170             if (bufferG2D == null) {
171                 bufferG2D = img.createGraphics();
172                 bufferG2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
173                 bufferG2D.setClip(clippath);
174             }
175             g2d = bufferG2D;
176 
177             float[] ctrlpts;
178             for (int i = 0; i < animpts.length; i += 2) {
179                 animate(animpts, deltas, i, size.width);
180                 animate(animpts, deltas, i + 1, size.height);
181             }
182             ctrlpts = animpts;
183             int len = ctrlpts.length;
184             gp.reset();
185             float prevx = ctrlpts[len - 2];
186             float prevy = ctrlpts[len - 1];
187             float curx = ctrlpts[0];
188             float cury = ctrlpts[1];
189             float midx = (curx + prevx) / 2.0f;
190             float midy = (cury + prevy) / 2.0f;
191             gp.moveTo(midx, midy);
192             for (int i = 2; i <= ctrlpts.length; i += 2) {
193                 float x1 = (midx + curx) / 2.0f;
194                 float y1 = (midy + cury) / 2.0f;
195                 prevx = curx;
196                 prevy = cury;
197                 if (i < ctrlpts.length) {
198                     curx = ctrlpts[i];
199                     cury = ctrlpts[i + 1];
200                 } else {
201                     curx = ctrlpts[0];
202                     cury = ctrlpts[1];
203                 }
204                 midx = (curx + prevx) / 2.0f;
205                 midy = (cury + prevy) / 2.0f;
206                 float x2 = (prevx + midx) / 2.0f;
207                 float y2 = (prevy + midy) / 2.0f;
208                 gp.curveTo(x1, y1, x2, y2, midx, midy);
209             }
210             gp.closePath();
211 
212             synchronized (lock) {
213                 g2d.setComposite(set);
214                 g2d.setBackground(getBezierColor(BACKGROUND));
215                 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
216 
217                 // g2d.clearRect(bounds.x-5, bounds.y-5, bounds.x + bounds.width
218                 // + 5, bounds.y + bounds.height + 5);
219                 g2d.clearRect(0, 0, getWidth(), getHeight());
220 
221                 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
222                 g2d.setColor(getBezierColor(OUTER));
223                 g2d.setComposite(opaque);
224                 g2d.setStroke(solid);
225                 g2d.draw(gp);
226                 g2d.setPaint(gradient);
227 
228                 Rectangle bounds = gp.getBounds();
229 
230                 gradient = new GradientPaint(bounds.x, bounds.y, getBezierColor(GRADIENT_A), bounds.x + bounds.width,
231                         bounds.y + bounds.height, getBezierColor(GRADIENT_B), true);
232 
233                 g2d.setComposite(blend);
234                 g2d.fill(gp);
235             }
236 
237             try {
238                 SwingUtilities.invokeAndWait(new Runnable() {
239                     @Override
240                     public void run() {
241                         repaint();
242                     }
243                 });
244             } catch (InvocationTargetException | InterruptedException e) {
245                 e.printStackTrace();
246             }
247         }
248         if (g2d != null) {
249             g2d.dispose();
250         }
251     }
252 
paint(Graphics g)253     public void paint(Graphics g) {
254         synchronized (lock) {
255             Graphics2D g2d = (Graphics2D) g;
256             if (img != null) {
257                 g2d.setComposite(AlphaComposite.Src);
258                 g2d.drawImage(img, null, 0, 0);
259             }
260         }
261     }
262 }
263