1 /*
2  * Copyright (c) 2007, 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 /*
25  * @test
26  * @bug 6296064 6521533
27  * @summary A simple manual test for visual verification of GradientPaint
28  * LinearGradientPaint, and RadialGradientPaint.
29  * @run main/manual MultiGradientTest
30  * @author campbelc
31  */
32 
33 import java.awt.BorderLayout;
34 import java.awt.Color;
35 import java.awt.Dimension;
36 import java.awt.GradientPaint;
37 import java.awt.Graphics;
38 import java.awt.Graphics2D;
39 import java.awt.LinearGradientPaint;
40 import java.awt.MultipleGradientPaint.ColorSpaceType;
41 import java.awt.MultipleGradientPaint.CycleMethod;
42 import java.awt.Paint;
43 import java.awt.Polygon;
44 import java.awt.RadialGradientPaint;
45 import java.awt.Rectangle;
46 import java.awt.RenderingHints;
47 import java.awt.geom.AffineTransform;
48 import java.awt.geom.NoninvertibleTransformException;
49 import java.awt.geom.Point2D;
50 import java.awt.event.ActionEvent;
51 import java.awt.event.ActionListener;
52 import java.awt.event.MouseAdapter;
53 import java.awt.event.MouseEvent;
54 import java.awt.event.WindowAdapter;
55 import java.awt.event.WindowEvent;
56 import java.util.Arrays;
57 import java.util.ArrayList;
58 import java.util.EnumSet;
59 import java.util.List;
60 import javax.swing.AbstractListModel;
61 import javax.swing.BoxLayout;
62 import javax.swing.ComboBoxModel;
63 import javax.swing.JCheckBox;
64 import javax.swing.JComboBox;
65 import javax.swing.JFrame;
66 import javax.swing.JLabel;
67 import javax.swing.JPanel;
68 import javax.swing.JSpinner;
69 import javax.swing.SpinnerNumberModel;
70 import javax.swing.event.ChangeEvent;
71 import javax.swing.event.ChangeListener;
72 
73 public class MultiGradientTest extends JPanel {
74 
75     private static final Color[] COLORS = {
76         new Color(0, 0, 0),
77         new Color(128, 128, 128),
78         new Color(255, 0, 0),
79         new Color(255, 255, 0),
80         new Color(0, 255, 0),
81         new Color(0, 255, 255),
82         new Color(128, 0, 255),
83         new Color(128, 128, 128),
84     };
85 
86     private static enum PaintType {BASIC, LINEAR, RADIAL};
87     private static enum ShapeType {RECT, ELLIPSE, MULTIPLE};
88     private static enum XformType {IDENTITY, TRANSLATE, SCALE, SHEAR, ROTATE};
89 
90     private PaintType paintType = PaintType.LINEAR;
91     private ShapeType shapeType = ShapeType.RECT;
92     private XformType xformType = XformType.IDENTITY;
93     private CycleMethod cycleMethod = CycleMethod.NO_CYCLE;
94     private ColorSpaceType colorSpace = ColorSpaceType.SRGB;
95     private Object antialiasHint = RenderingHints.VALUE_ANTIALIAS_OFF;
96     private Object renderHint = RenderingHints.VALUE_RENDER_SPEED;
97     private AffineTransform transform = new AffineTransform();
98 
99     private int numColors;
100 
101     private GradientPanel gradientPanel;
102     private ControlsPanel controlsPanel;
103 
MultiGradientTest()104     private MultiGradientTest() {
105         numColors = COLORS.length;
106 
107         setLayout(new BorderLayout());
108         gradientPanel = new GradientPanel();
109         add(gradientPanel, BorderLayout.CENTER);
110         controlsPanel = new ControlsPanel();
111         add(controlsPanel, BorderLayout.SOUTH);
112     }
113 
114     private class GradientPanel extends JPanel {
115         private int startX, startY, endX, endY;
116         private int ctrX, ctrY, focusX, focusY;
117         private float radius;
118         private Paint paint;
119 
GradientPanel()120         private GradientPanel() {
121             startX = 20;
122             startY = 20;
123             endX   = 100;
124             endY   = 100;
125             ctrX   = 100;
126             ctrY   = 100;
127             focusX = 100;
128             focusY = 100;
129             radius = 100.0f;
130 
131             makeNewPaint();
132 
133             MouseAdapter l = new MyMouseAdapter();
134             addMouseListener(l);
135             addMouseMotionListener(l);
136         }
137 
paintComponent(Graphics g)138         public void paintComponent(Graphics g) {
139             Graphics2D g2d = (Graphics2D)g.create();
140 
141             int w = getWidth();
142             int h = getHeight();
143             g2d.setColor(Color.black);
144             g2d.fillRect(0, 0, w, h);
145 
146             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
147                                  antialiasHint);
148             g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
149                                  renderHint);
150 
151             g2d.transform(transform);
152             g2d.setPaint(paint);
153 
154             switch (shapeType) {
155             default:
156             case RECT:
157                 g2d.fillRect(0, 0, w, h);
158                 break;
159 
160             case ELLIPSE:
161                 g2d.fillOval(0, 0, w, h);
162                 break;
163 
164             case MULTIPLE:
165                 g2d.fillRect(0, 0, w/2, h/2);
166                 g2d.fillOval(w/2, 0, w/2, h/2);
167                 g2d.drawOval(0, h/2, w/2, h/2);
168                 g2d.drawLine(0, h/2, w/2, h);
169                 g2d.drawLine(0, h, w/2, h/2);
170                 Polygon p = new Polygon();
171                 p.addPoint(w/2, h);
172                 p.addPoint(w, h);
173                 p.addPoint(3*w/4, h/2);
174                 g2d.fillPolygon(p);
175                 break;
176             }
177 
178             switch (paintType) {
179             default:
180             case BASIC:
181             case LINEAR:
182                 g2d.setColor(Color.white);
183                 g2d.fillRect(startX-1, startY-1, 2, 2);
184                 g2d.drawString("1", startX, startY + 12);
185                 g2d.fillRect(endX-1, endY-1, 2, 2);
186                 g2d.drawString("2", endX, endY + 12);
187                 break;
188 
189             case RADIAL:
190                 g2d.setColor(Color.white);
191                 g2d.fillRect(ctrX-1, ctrY-1, 2, 2);
192                 g2d.drawString("C", ctrX, ctrY + 12);
193                 g2d.fillRect(focusX-1, focusY-1, 2, 2);
194                 g2d.drawString("F", focusX, focusY + 12);
195                 break;
196             }
197 
198             g2d.dispose();
199         }
200 
updatePoints(int x, int y)201         private void updatePoints(int x, int y) {
202             Point2D inv = new Point2D.Double(x, y);
203 
204             try {
205                 inv = transform.inverseTransform(inv, null);
206             } catch (NoninvertibleTransformException e) {
207                 e.printStackTrace();
208             }
209 
210             x = (int)inv.getX();
211             y = (int)inv.getY();
212 
213             switch (paintType) {
214             default:
215             case BASIC:
216             case LINEAR:
217                 // pick the closest point to move
218                 if (inv.distance(startX, startY) < inv.distance(endX, endY)) {
219                     startX = x;
220                     startY = y;
221                 } else {
222                     endX = x;
223                     endY = y;
224                 }
225                 break;
226 
227             case RADIAL:
228                 // pick the closest point to move
229                 if (inv.distance(ctrX, ctrY) < inv.distance(focusX, focusY)) {
230                     ctrX = x;
231                     ctrY = y;
232                 } else {
233                     focusX = x;
234                     focusY = y;
235                 }
236                 break;
237             }
238 
239             updatePaint();
240         }
241 
makeNewPaint()242         private void makeNewPaint() {
243             Color[] colors = Arrays.copyOf(COLORS, numColors);
244             float[] fractions = new float[colors.length];
245             for (int i = 0; i < fractions.length; i++) {
246                 fractions[i] = ((float)i) / (fractions.length-1);
247             }
248 
249             switch (paintType) {
250             case BASIC:
251                 boolean cyclic = (cycleMethod != CycleMethod.NO_CYCLE);
252                 paint = new GradientPaint(startX, startY, Color.RED,
253                                           endX, endY, Color.BLUE, cyclic);
254                 break;
255 
256             default:
257             case LINEAR:
258                 paint =
259                     new LinearGradientPaint(new Point2D.Float(startX, startY),
260                                             new Point2D.Float(endX, endY),
261                                             fractions, colors,
262                                             cycleMethod, colorSpace,
263                                             new AffineTransform());
264                 break;
265 
266             case RADIAL:
267                 paint =
268                     new RadialGradientPaint(new Point2D.Float(ctrX, ctrY),
269                                             radius,
270                                             new Point2D.Float(focusX, focusY),
271                                             fractions, colors,
272                                             cycleMethod, colorSpace,
273                                             new AffineTransform());
274                 break;
275             }
276 
277             switch (xformType) {
278             default:
279             case IDENTITY:
280                 transform = new AffineTransform();
281                 break;
282             case TRANSLATE:
283                 transform = AffineTransform.getTranslateInstance(2, 2);
284                 break;
285             case SCALE:
286                 transform = AffineTransform.getScaleInstance(1.2, 1.4);
287                 break;
288             case SHEAR:
289                 transform = AffineTransform.getShearInstance(0.1, 0.1);
290                 break;
291             case ROTATE:
292                 transform = AffineTransform.getRotateInstance(Math.PI / 4,
293                                                               getWidth()/2,
294                                                               getHeight()/2);
295                 break;
296             }
297         }
298 
updatePaint()299         public void updatePaint() {
300             makeNewPaint();
301             repaint();
302         }
303 
304         private class MyMouseAdapter extends MouseAdapter {
305             @Override
mouseClicked(MouseEvent e)306             public void mouseClicked(MouseEvent e) {
307                 updatePoints(e.getX(), e.getY());
308             }
309 
310             @Override
mouseDragged(MouseEvent e)311             public void mouseDragged(MouseEvent e) {
312                 updatePoints(e.getX(), e.getY());
313             }
314         }
315 
getPreferredSize()316         public Dimension getPreferredSize() {
317             return new Dimension(400, 400);
318         }
319     }
320 
321     private class ControlsPanel extends JPanel implements ActionListener {
322         private JComboBox cmbPaint, cmbCycle, cmbSpace, cmbShape, cmbXform;
323         private JCheckBox cbAntialias, cbRender;
324         private JSpinner spinNumColors;
325 
ControlsPanel()326         private ControlsPanel() {
327             cmbPaint = createCombo(this, paintType);
328             cmbPaint.setSelectedIndex(1);
329             cmbCycle = createCombo(this, cycleMethod);
330             cmbSpace = createCombo(this, colorSpace);
331             cmbShape = createCombo(this, shapeType);
332             cmbXform = createCombo(this, xformType);
333 
334             int max = COLORS.length;
335             SpinnerNumberModel model = new SpinnerNumberModel(max, 2, max, 1);
336             spinNumColors = new JSpinner(model);
337             spinNumColors.addChangeListener(new ChangeListener() {
338                 public void stateChanged(ChangeEvent e) {
339                     numColors = ((Integer)spinNumColors.getValue()).intValue();
340                     gradientPanel.updatePaint();
341                 }
342             });
343             add(spinNumColors);
344 
345             cbAntialias = createCheck(this, "Antialiasing");
346             cbRender = createCheck(this, "Render Quality");
347         }
348 
createCombo(JPanel panel, Enum e)349         private JComboBox createCombo(JPanel panel, Enum e) {
350             JComboBox cmb = new JComboBox();
351             cmb.setModel(new EnumComboBoxModel(e.getClass()));
352             cmb.addActionListener(this);
353             panel.add(cmb);
354             return cmb;
355         }
356 
createCheck(JPanel panel, String name)357         private JCheckBox createCheck(JPanel panel, String name) {
358             JCheckBox cb = new JCheckBox(name);
359             cb.addActionListener(this);
360             panel.add(cb);
361             return cb;
362         }
363 
actionPerformed(ActionEvent e)364         public void actionPerformed(ActionEvent e) {
365             Object source = e.getSource();
366 
367             if (source == cmbPaint) {
368                 paintType = (PaintType)cmbPaint.getSelectedItem();
369             } else if (source == cmbCycle) {
370                 cycleMethod = (CycleMethod)cmbCycle.getSelectedItem();
371             } else if (source == cmbSpace) {
372                 colorSpace = (ColorSpaceType)cmbSpace.getSelectedItem();
373             } else if (source == cmbShape) {
374                 shapeType = (ShapeType)cmbShape.getSelectedItem();
375             } else if (source == cmbXform) {
376                 xformType = (XformType)cmbXform.getSelectedItem();
377             } else if (source == cbAntialias) {
378                 antialiasHint = cbAntialias.isSelected() ?
379                     RenderingHints.VALUE_ANTIALIAS_ON :
380                     RenderingHints.VALUE_ANTIALIAS_OFF;
381             } else if (source == cbRender) {
382                 renderHint = cbRender.isSelected() ?
383                     RenderingHints.VALUE_RENDER_QUALITY :
384                     RenderingHints.VALUE_RENDER_SPEED;
385             }
386 
387             gradientPanel.updatePaint();
388         }
389     }
390 
391     private static class EnumComboBoxModel<E extends Enum<E>>
392         extends AbstractListModel
393         implements ComboBoxModel
394     {
395         private E selected = null;
396         private List<E> list;
397 
EnumComboBoxModel(Class<E> en)398         public EnumComboBoxModel(Class<E> en) {
399             EnumSet<E> ens = EnumSet.allOf(en);
400             list = new ArrayList<E>(ens);
401             selected = list.get(0);
402         }
403 
getSize()404         public int getSize() {
405             return list.size();
406         }
407 
getElementAt(int index)408         public E getElementAt(int index) {
409             return list.get(index);
410         }
411 
setSelectedItem(Object anItem)412         public void setSelectedItem(Object anItem) {
413             selected = (E)anItem;
414             this.fireContentsChanged(this, 0, getSize());
415         }
416 
getSelectedItem()417         public E getSelectedItem() {
418             return selected;
419         }
420     }
421 
main(String[] args)422     public static void main(String[] args) {
423         final JFrame frame = new JFrame("Multistop Gradient Demo");
424         frame.addWindowListener(new WindowAdapter() {
425             public void windowClosing(WindowEvent e) {
426                 frame.dispose();
427             }
428         });
429         frame.add(new MultiGradientTest());
430         frame.pack();
431         frame.setLocationRelativeTo(null);
432         frame.setVisible(true);
433     }
434 }
435