1/*
2 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *   - Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 *
11 *   - Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 *
15 *   - Neither the name of Oracle nor the names of its
16 *     contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This source code is provided to illustrate the usage of a given feature
34 * or technique and has been deliberately simplified. Additional steps
35 * required for a production-quality application, such as security checks,
36 * input validation and proper error handling, might not be present in
37 * this sample code.
38 */
39
40
41package j2dbench.tests;
42
43import java.awt.Graphics;
44import java.awt.Graphics2D;
45import java.awt.Color;
46import java.awt.Dimension;
47import java.awt.AlphaComposite;
48import java.awt.Stroke;
49import java.awt.BasicStroke;
50import java.awt.GradientPaint;
51import java.awt.LinearGradientPaint;
52import java.awt.MultipleGradientPaint;
53import java.awt.MultipleGradientPaint.CycleMethod;
54import java.awt.MultipleGradientPaint.ColorSpaceType;
55import java.awt.RadialGradientPaint;
56import java.awt.RenderingHints;
57import java.awt.TexturePaint;
58import java.awt.geom.CubicCurve2D;
59import java.awt.geom.Ellipse2D;
60import java.awt.geom.Point2D;
61import java.awt.geom.Rectangle2D;
62import java.awt.image.BufferedImage;
63import java.io.PrintWriter;
64import java.util.ArrayList;
65import javax.swing.JComponent;
66
67import j2dbench.Group;
68import j2dbench.Node;
69import j2dbench.Option;
70import j2dbench.TestEnvironment;
71
72public abstract class RenderTests extends GraphicsTests {
73    static Group renderroot;
74    static Group renderoptroot;
75    static Group rendertestroot;
76    static Group rendershaperoot;
77
78    static Option paintList;
79    static Option doAntialias;
80    static Option doAlphaColors;
81    static Option sizeList;
82    static Option strokeList;
83
84    static final int NUM_RANDOMCOLORS = 4096;
85    static final int NUM_RANDOMCOLORMASK = (NUM_RANDOMCOLORS - 1);
86    static Color randAlphaColors[];
87    static Color randOpaqueColors[];
88
89    static {
90        randOpaqueColors = new Color[NUM_RANDOMCOLORS];
91        randAlphaColors = new Color[NUM_RANDOMCOLORS];
92        for (int i = 0; i < NUM_RANDOMCOLORS; i++) {
93            int r = (int) (Math.random() * 255);
94            int g = (int) (Math.random() * 255);
95            int b = (int) (Math.random() * 255);
96            randOpaqueColors[i] = new Color(r, g, b);
97            randAlphaColors[i] = makeAlphaColor(randOpaqueColors[i], 32);
98        }
99    }
100
101    static boolean hasMultiGradient;
102
103    static {
104        try {
105            hasMultiGradient = (MultipleGradientPaint.class != null);
106        } catch (NoClassDefFoundError e) {
107        }
108    }
109
110    public static void init() {
111        renderroot = new Group(graphicsroot, "render", "Rendering Benchmarks");
112        renderoptroot = new Group(renderroot, "opts", "Rendering Options");
113        rendertestroot = new Group(renderroot, "tests", "Rendering Tests");
114
115        ArrayList paintStrs = new ArrayList();
116        ArrayList paintDescs = new ArrayList();
117        paintStrs.add("single");
118        paintDescs.add("Single Color");
119        paintStrs.add("random");
120        paintDescs.add("Random Color");
121        if (hasGraphics2D) {
122            paintStrs.add("gradient2");
123            paintDescs.add("2-color GradientPaint");
124            if (hasMultiGradient) {
125                paintStrs.add("linear2");
126                paintDescs.add("2-color LinearGradientPaint");
127                paintStrs.add("linear3");
128                paintDescs.add("3-color LinearGradientPaint");
129                paintStrs.add("radial2");
130                paintDescs.add("2-color RadialGradientPaint");
131                paintStrs.add("radial3");
132                paintDescs.add("3-color RadialGradientPaint");
133            }
134            paintStrs.add("texture20");
135            paintDescs.add("20x20 TexturePaint");
136            paintStrs.add("texture32");
137            paintDescs.add("32x32 TexturePaint");
138        }
139        String[] paintStrArr = new String[paintStrs.size()];
140        paintStrArr = (String[])paintStrs.toArray(paintStrArr);
141        String[] paintDescArr = new String[paintDescs.size()];
142        paintDescArr = (String[])paintDescs.toArray(paintDescArr);
143        paintList =
144            new Option.ObjectList(renderoptroot,
145                                  "paint", "Paint Type",
146                                  paintStrArr, paintStrArr,
147                                  paintStrArr, paintDescArr,
148                                  0x1);
149        ((Option.ObjectList) paintList).setNumRows(5);
150
151        // add special RandomColorOpt for backwards compatibility with
152        // older options files
153        new RandomColorOpt();
154
155        if (hasGraphics2D) {
156            doAlphaColors =
157                new Option.Toggle(renderoptroot, "alphacolor",
158                                  "Set the alpha of the paint to 0.125",
159                                  Option.Toggle.Off);
160            doAntialias =
161                new Option.Toggle(renderoptroot, "antialias",
162                                  "Render shapes antialiased",
163                                  Option.Toggle.Off);
164            String strokeStrings[] = {
165                "width0",
166                "width1",
167                "width5",
168                "width20",
169                "dash0_5",
170                "dash1_5",
171                "dash5_20",
172                "dash20_50",
173            };
174            String strokeDescriptions[] = {
175                "Solid Thin lines",
176                "Solid Width 1 lines",
177                "Solid Width 5 lines",
178                "Solid Width 20 lines",
179                "Dashed Thin lines",
180                "Dashed Width 1 lines",
181                "Dashed Width 5 lines",
182                "Dashed Width 20 lines",
183            };
184            BasicStroke strokeObjects[] = {
185                new BasicStroke(0f),
186                new BasicStroke(1f),
187                new BasicStroke(5f),
188                new BasicStroke(20f),
189                new BasicStroke(0f, BasicStroke.CAP_SQUARE,
190                                BasicStroke.JOIN_MITER, 10f,
191                                new float[] { 5f, 5f }, 0f),
192                new BasicStroke(1f, BasicStroke.CAP_SQUARE,
193                                BasicStroke.JOIN_MITER, 10f,
194                                new float[] { 5f, 5f }, 0f),
195                new BasicStroke(5f, BasicStroke.CAP_SQUARE,
196                                BasicStroke.JOIN_MITER, 10f,
197                                new float[] { 20f, 20f }, 0f),
198                new BasicStroke(20f, BasicStroke.CAP_SQUARE,
199                                BasicStroke.JOIN_MITER, 10f,
200                                new float[] { 50f, 50f }, 0f),
201            };
202            strokeList =
203                new Option.ObjectList(renderoptroot,
204                                      "stroke", "Stroke Type",
205                                      strokeStrings, strokeObjects,
206                                      strokeStrings, strokeDescriptions,
207                                      0x2);
208            ((Option.ObjectList) strokeList).setNumRows(4);
209        }
210
211        new DrawDiagonalLines();
212        new DrawHorizontalLines();
213        new DrawVerticalLines();
214        new FillRects();
215        new DrawRects();
216        new FillOvals();
217        new DrawOvals();
218        new FillPolys();
219        new DrawPolys();
220
221        if (hasGraphics2D) {
222            rendershaperoot = new Group(rendertestroot, "shape",
223                                        "Shape Rendering Tests");
224
225            new FillCubics();
226            new DrawCubics();
227            new FillEllipse2Ds();
228            new DrawEllipse2Ds();
229        }
230    }
231
232    /**
233     * This "virtual Node" implementation is here to maintain backward
234     * compatibility with older J2DBench releases, specifically those
235     * options files that were created before we added the gradient/texture
236     * paint options in JDK 6.  This class will translate the color settings
237     * from the old "randomcolor" option into the new "paint" option.
238     */
239    private static class RandomColorOpt extends Node {
240        public RandomColorOpt() {
241            super(renderoptroot, "randomcolor",
242                  "Use random colors for each shape");
243        }
244
245        public JComponent getJComponent() {
246            return null;
247        }
248
249        public void restoreDefault() {
250            // no-op
251        }
252
253        public void write(PrintWriter pw) {
254            // no-op (the random/single choice will be saved as part of
255            // the new "paint" option added to J2DBench in JDK 6)
256        }
257
258        public String setOption(String key, String value) {
259            String opts;
260            if (value.equals("On")) {
261                opts = "random";
262            } else if (value.equals("Off")) {
263                opts = "single";
264            } else if (value.equals("Both")) {
265                opts = "random,single";
266            } else {
267                return "Bad value";
268            }
269            return ((Option.ObjectList)paintList).setValueFromString(opts);
270        }
271    }
272
273    public static class Context extends GraphicsTests.Context {
274        int colorindex;
275        Color colorlist[];
276    }
277
278    public RenderTests(Group parent, String nodeName, String description) {
279        super(parent, nodeName, description);
280        addDependencies(renderoptroot, true);
281    }
282
283    public GraphicsTests.Context createContext() {
284        return new RenderTests.Context();
285    }
286
287    public void initContext(TestEnvironment env, GraphicsTests.Context ctx) {
288        super.initContext(env, ctx);
289        RenderTests.Context rctx = (RenderTests.Context) ctx;
290        boolean alphacolor;
291
292        if (hasGraphics2D) {
293            Graphics2D g2d = (Graphics2D) rctx.graphics;
294            if (env.isEnabled(doAntialias)) {
295                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
296                                     RenderingHints.VALUE_ANTIALIAS_ON);
297            }
298            alphacolor = env.isEnabled(doAlphaColors);
299            g2d.setStroke((Stroke) env.getModifier(strokeList));
300        } else {
301            alphacolor = false;
302        }
303
304        String paint = (String)env.getModifier(paintList);
305        if (paint.equals("single")) {
306            Color c = Color.darkGray;
307            if (alphacolor) {
308                c = makeAlphaColor(c, 32);
309            }
310            rctx.graphics.setColor(c);
311        } else if (paint.equals("random")) {
312            rctx.colorlist = alphacolor ? randAlphaColors : randOpaqueColors;
313        } else if (paint.equals("gradient2")) {
314            Color[] colors = makeGradientColors(2, alphacolor);
315            Graphics2D g2d = (Graphics2D)rctx.graphics;
316            g2d.setPaint(new GradientPaint(0.0f, 0.0f, colors[0],
317                                           10.0f, 10.0f, colors[1], true));
318        } else if (paint.equals("linear2")) {
319            Graphics2D g2d = (Graphics2D)rctx.graphics;
320            g2d.setPaint(makeLinear(2, alphacolor));
321        } else if (paint.equals("linear3")) {
322            Graphics2D g2d = (Graphics2D)rctx.graphics;
323            g2d.setPaint(makeLinear(3, alphacolor));
324        } else if (paint.equals("radial2")) {
325            Graphics2D g2d = (Graphics2D)rctx.graphics;
326            g2d.setPaint(makeRadial(2, alphacolor));
327        } else if (paint.equals("radial3")) {
328            Graphics2D g2d = (Graphics2D)rctx.graphics;
329            g2d.setPaint(makeRadial(3, alphacolor));
330        } else if (paint.equals("texture20")) {
331            Graphics2D g2d = (Graphics2D)rctx.graphics;
332            g2d.setPaint(makeTexturePaint(20, alphacolor));
333        } else if (paint.equals("texture32")) {
334            Graphics2D g2d = (Graphics2D)rctx.graphics;
335            g2d.setPaint(makeTexturePaint(32, alphacolor));
336        } else {
337            throw new InternalError("Invalid paint mode");
338        }
339    }
340
341    private Color[] makeGradientColors(int numColors, boolean alpha) {
342        Color[] colors = new Color[] {Color.red, Color.blue,
343                                      Color.green, Color.yellow};
344        Color[] ret = new Color[numColors];
345        for (int i = 0; i < numColors; i++) {
346            ret[i] = alpha ? makeAlphaColor(colors[i], 32) : colors[i];
347        }
348        return ret;
349    }
350
351    private LinearGradientPaint makeLinear(int numColors, boolean alpha) {
352        float interval = 1.0f / (numColors - 1);
353        float[] fractions = new float[numColors];
354        for (int i = 0; i < fractions.length; i++) {
355            fractions[i] = i * interval;
356        }
357        Color[] colors = makeGradientColors(numColors, alpha);
358        return new LinearGradientPaint(0.0f, 0.0f,
359                                       10.0f, 10.0f,
360                                       fractions, colors,
361                                       CycleMethod.REFLECT);
362    }
363
364    private RadialGradientPaint makeRadial(int numColors, boolean alpha) {
365        float interval = 1.0f / (numColors - 1);
366        float[] fractions = new float[numColors];
367        for (int i = 0; i < fractions.length; i++) {
368            fractions[i] = i * interval;
369        }
370        Color[] colors = makeGradientColors(numColors, alpha);
371        return new RadialGradientPaint(0.0f, 0.0f, 10.0f,
372                                       fractions, colors,
373                                       CycleMethod.REFLECT);
374    }
375
376    private TexturePaint makeTexturePaint(int size, boolean alpha) {
377        int s2 = size / 2;
378        int type =
379            alpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
380        BufferedImage img = new BufferedImage(size, size, type);
381        Color[] colors = makeGradientColors(4, alpha);
382        Graphics2D g2d = img.createGraphics();
383        g2d.setComposite(AlphaComposite.Src);
384        g2d.setColor(colors[0]);
385        g2d.fillRect(0, 0, s2, s2);
386        g2d.setColor(colors[1]);
387        g2d.fillRect(s2, 0, s2, s2);
388        g2d.setColor(colors[3]);
389        g2d.fillRect(0, s2, s2, s2);
390        g2d.setColor(colors[2]);
391        g2d.fillRect(s2, s2, s2, s2);
392        g2d.dispose();
393        Rectangle2D bounds = new Rectangle2D.Float(0, 0, size, size);
394        return new TexturePaint(img, bounds);
395    }
396
397    public static class DrawDiagonalLines extends RenderTests {
398        public DrawDiagonalLines() {
399            super(rendertestroot, "drawLine", "Draw Diagonal Lines");
400        }
401
402        public int pixelsTouched(GraphicsTests.Context ctx) {
403            return Math.max(ctx.outdim.width, ctx.outdim.height);
404        }
405
406        public void runTest(Object ctx, int numReps) {
407            RenderTests.Context rctx = (RenderTests.Context) ctx;
408            int size = rctx.size - 1;
409            int x = rctx.initX;
410            int y = rctx.initY;
411            Graphics g = rctx.graphics;
412            g.translate(rctx.orgX, rctx.orgY);
413            Color rCArray[] = rctx.colorlist;
414            int ci = rctx.colorindex;
415            if (rctx.animate) {
416                do {
417                    if (rCArray != null) {
418                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
419                    }
420                    g.drawLine(x, y, x + size, y + size);
421                    if ((x -= 3) < 0) x += rctx.maxX;
422                    if ((y -= 1) < 0) y += rctx.maxY;
423                } while (--numReps > 0);
424            } else {
425                do {
426                    if (rCArray != null) {
427                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
428                    }
429                    g.drawLine(x, y, x + size, y + size);
430                } while (--numReps > 0);
431            }
432            rctx.colorindex = ci;
433            g.translate(-rctx.orgX, -rctx.orgY);
434        }
435    }
436
437    public static class DrawHorizontalLines extends RenderTests {
438        public DrawHorizontalLines() {
439            super(rendertestroot, "drawLineHoriz",
440                  "Draw Horizontal Lines");
441        }
442
443        public int pixelsTouched(GraphicsTests.Context ctx) {
444            return ctx.outdim.width;
445        }
446
447        public Dimension getOutputSize(int w, int h) {
448            return new Dimension(w, 1);
449        }
450
451        public void runTest(Object ctx, int numReps) {
452            RenderTests.Context rctx = (RenderTests.Context) ctx;
453            int size = rctx.size - 1;
454            int x = rctx.initX;
455            int y = rctx.initY;
456            Graphics g = rctx.graphics;
457            g.translate(rctx.orgX, rctx.orgY);
458            Color rCArray[] = rctx.colorlist;
459            int ci = rctx.colorindex;
460            if (rctx.animate) {
461                do {
462                    if (rCArray != null) {
463                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
464                    }
465                    g.drawLine(x, y, x + size, y);
466                    if ((x -= 3) < 0) x += rctx.maxX;
467                    if ((y -= 1) < 0) y += rctx.maxY;
468                } while (--numReps > 0);
469            } else {
470                do {
471                    if (rCArray != null) {
472                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
473                    }
474                    g.drawLine(x, y, x + size, y);
475                } while (--numReps > 0);
476            }
477            rctx.colorindex = ci;
478            g.translate(-rctx.orgX, -rctx.orgY);
479        }
480    }
481
482    public static class DrawVerticalLines extends RenderTests {
483        public DrawVerticalLines() {
484            super(rendertestroot, "drawLineVert",
485                  "Draw Vertical Lines");
486        }
487
488        public int pixelsTouched(GraphicsTests.Context ctx) {
489            return ctx.outdim.height;
490        }
491
492        public Dimension getOutputSize(int w, int h) {
493            return new Dimension(1, h);
494        }
495
496        public void runTest(Object ctx, int numReps) {
497            RenderTests.Context rctx = (RenderTests.Context) ctx;
498            int size = rctx.size - 1;
499            int x = rctx.initX;
500            int y = rctx.initY;
501            Graphics g = rctx.graphics;
502            g.translate(rctx.orgX, rctx.orgY);
503            Color rCArray[] = rctx.colorlist;
504            int ci = rctx.colorindex;
505            if (rctx.animate) {
506                do {
507                    if (rCArray != null) {
508                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
509                    }
510                    g.drawLine(x, y, x, y + size);
511                    if ((x -= 3) < 0) x += rctx.maxX;
512                    if ((y -= 1) < 0) y += rctx.maxY;
513                } while (--numReps > 0);
514            } else {
515                do {
516                    if (rCArray != null) {
517                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
518                    }
519                    g.drawLine(x, y, x, y + size);
520                } while (--numReps > 0);
521            }
522            rctx.colorindex = ci;
523            g.translate(-rctx.orgX, -rctx.orgY);
524        }
525    }
526
527    public static class FillRects extends RenderTests {
528        public FillRects() {
529            super(rendertestroot, "fillRect", "Fill Rectangles");
530        }
531
532        public void runTest(Object ctx, int numReps) {
533            RenderTests.Context rctx = (RenderTests.Context) ctx;
534            int size = rctx.size;
535            int x = rctx.initX;
536            int y = rctx.initY;
537            Graphics g = rctx.graphics;
538            g.translate(rctx.orgX, rctx.orgY);
539            Color rCArray[] = rctx.colorlist;
540            int ci = rctx.colorindex;
541            if (rctx.animate) {
542                do {
543                    if (rCArray != null) {
544                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
545                    }
546                    g.fillRect(x, y, size, size);
547                    if ((x -= 3) < 0) x += rctx.maxX;
548                    if ((y -= 1) < 0) y += rctx.maxY;
549                } while (--numReps > 0);
550            } else {
551                do {
552                    if (rCArray != null) {
553                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
554                    }
555                    g.fillRect(x, y, size, size);
556                } while (--numReps > 0);
557            }
558            rctx.colorindex = ci;
559            g.translate(-rctx.orgX, -rctx.orgY);
560        }
561    }
562
563    public static class DrawRects extends RenderTests {
564        public DrawRects() {
565            super(rendertestroot, "drawRect", "Draw Rectangles");
566        }
567
568        public int pixelsTouched(GraphicsTests.Context ctx) {
569            int w = ctx.outdim.width;
570            int h = ctx.outdim.height;
571            if (w < 2 || h < 2) {
572                // If one dimension is less than 2 then there is no
573                // gap in the middle, so we get a solid filled rectangle.
574                return w * h;
575            }
576            return (w * 2) + ((h - 2) * 2);
577        }
578
579        public void runTest(Object ctx, int numReps) {
580            RenderTests.Context rctx = (RenderTests.Context) ctx;
581            int size = rctx.size - 1;
582            int x = rctx.initX;
583            int y = rctx.initY;
584            Graphics g = rctx.graphics;
585            g.translate(rctx.orgX, rctx.orgY);
586            Color rCArray[] = rctx.colorlist;
587            int ci = rctx.colorindex;
588            if (rctx.animate) {
589                do {
590                    if (rCArray != null) {
591                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
592                    }
593                    g.drawRect(x, y, size, size);
594                    if ((x -= 3) < 0) x += rctx.maxX;
595                    if ((y -= 1) < 0) y += rctx.maxY;
596                } while (--numReps > 0);
597            } else {
598                do {
599                    if (rCArray != null) {
600                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
601                    }
602                    g.drawRect(x, y, size, size);
603                } while (--numReps > 0);
604            }
605            rctx.colorindex = ci;
606            g.translate(-rctx.orgX, -rctx.orgY);
607        }
608    }
609
610    public static class FillOvals extends RenderTests {
611        public FillOvals() {
612            super(rendertestroot, "fillOval", "Fill Ellipses");
613        }
614
615        public int pixelsTouched(GraphicsTests.Context ctx) {
616            // Approximated
617            double xaxis = ctx.outdim.width / 2.0;
618            double yaxis = ctx.outdim.height / 2.0;
619            return (int) (xaxis * yaxis * Math.PI);
620        }
621
622        public void runTest(Object ctx, int numReps) {
623            RenderTests.Context rctx = (RenderTests.Context) ctx;
624            int size = rctx.size;
625            int x = rctx.initX;
626            int y = rctx.initY;
627            Graphics g = rctx.graphics;
628            g.translate(rctx.orgX, rctx.orgY);
629            Color rCArray[] = rctx.colorlist;
630            int ci = rctx.colorindex;
631            if (rctx.animate) {
632                do {
633                    if (rCArray != null) {
634                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
635                    }
636                    g.fillOval(x, y, size, size);
637                    if ((x -= 3) < 0) x += rctx.maxX;
638                    if ((y -= 1) < 0) y += rctx.maxY;
639                } while (--numReps > 0);
640            } else {
641                do {
642                    if (rCArray != null) {
643                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
644                    }
645                    g.fillOval(x, y, size, size);
646                } while (--numReps > 0);
647            }
648            rctx.colorindex = ci;
649            g.translate(-rctx.orgX, -rctx.orgY);
650        }
651    }
652
653    public static class DrawOvals extends RenderTests {
654        public DrawOvals() {
655            super(rendertestroot, "drawOval", "Draw Ellipses");
656        }
657
658        public int pixelsTouched(GraphicsTests.Context ctx) {
659            /*
660             * Approximation: We figured that the vertical chord connecting
661             * the +45 deg and -45 deg points on the ellipse is about
662             * height/sqrt(2) pixels long.  Likewise, the horizontal chord
663             * connecting the +45 deg and +135 deg points on the ellipse is
664             * about width/sqrt(2) pixels long.  Each of these chords has
665             * a parallel on the opposite side of the respective axis (there
666             * are two horizontal chords and two vertical chords).  Altogether
667             * this gives a reasonable approximation of the total number of
668             * pixels touched by the ellipse, so we have:
669             *     2*(w/sqrt(2)) + 2*(h/sqrt(2))
670             *  == (2/sqrt(2))*(w+h)
671             *  == (sqrt(2))*(w+h)
672             */
673            return (int)(Math.sqrt(2.0)*(ctx.outdim.width+ctx.outdim.height));
674        }
675
676        public void runTest(Object ctx, int numReps) {
677            RenderTests.Context rctx = (RenderTests.Context) ctx;
678            int size = rctx.size - 1;
679            int x = rctx.initX;
680            int y = rctx.initY;
681            Graphics g = rctx.graphics;
682            g.translate(rctx.orgX, rctx.orgY);
683            Color rCArray[] = rctx.colorlist;
684            int ci = rctx.colorindex;
685            if (rctx.animate) {
686                do {
687                    if (rCArray != null) {
688                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
689                    }
690                    g.drawOval(x, y, size, size);
691                    if ((x -= 3) < 0) x += rctx.maxX;
692                    if ((y -= 1) < 0) y += rctx.maxY;
693                } while (--numReps > 0);
694            } else {
695                do {
696                    if (rCArray != null) {
697                        g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
698                    }
699                    g.drawOval(x, y, size, size);
700                } while (--numReps > 0);
701            }
702            rctx.colorindex = ci;
703            g.translate(-rctx.orgX, -rctx.orgY);
704        }
705    }
706
707    public static class FillPolys extends RenderTests {
708        public FillPolys() {
709            super(rendertestroot, "fillPoly", "Fill Hexagonal Polygons");
710        }
711
712        public int pixelsTouched(GraphicsTests.Context ctx) {
713            /*
714             * The polygon is a hexagon inscribed inside the square but
715             * missing a triangle at each of the four corners of size
716             * (w/4) by (h/2).
717             *
718             * Putting 2 of these triangles together gives a rectangle
719             * of size (w/4) by (h/2).
720             *
721             * Putting 2 of these rectangles together gives a total
722             * missing rectangle size of (w/2) by (h/2).
723             *
724             * Thus, exactly one quarter of the whole square is not
725             * touched by the filled polygon.
726             */
727            int size = ctx.outdim.width * ctx.outdim.height;
728            return size - (size / 4);
729        }
730
731        public void runTest(Object ctx, int numReps) {
732            RenderTests.Context rctx = (RenderTests.Context) ctx;
733            int size = rctx.size;
734            int x = rctx.initX;
735            int y = rctx.initY;
736            int hexaX[] = new int[6];
737            int hexaY[] = new int[6];
738            Graphics g = rctx.graphics;
739            g.translate(rctx.orgX, rctx.orgY);
740            Color rCArray[] = rctx.colorlist;
741            int ci = rctx.colorindex;
742            do {
743                hexaX[0] = x;
744                hexaX[1] = hexaX[5] = x+size/4;
745                hexaX[2] = hexaX[4] = x+size-size/4;
746                hexaX[3] = x+size;
747                hexaY[1] = hexaY[2] = y;
748                hexaY[0] = hexaY[3] = y+size/2;
749                hexaY[4] = hexaY[5] = y+size;
750
751                if (rCArray != null) {
752                    g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
753                }
754                g.fillPolygon(hexaX, hexaY, 6);
755                if ((x -= 3) < 0) x += rctx.maxX;
756                if ((y -= 1) < 0) y += rctx.maxY;
757            } while (--numReps > 0);
758            rctx.colorindex = ci;
759            g.translate(-rctx.orgX, -rctx.orgY);
760        }
761    }
762
763    public static class DrawPolys extends RenderTests {
764        public DrawPolys() {
765            super(rendertestroot, "drawPoly", "Draw Hexagonal Polygons");
766        }
767
768        public int pixelsTouched(GraphicsTests.Context ctx) {
769            /*
770             * The two horizontal segments have exactly two pixels per column.
771             * Since the diagonals are more vertical than horizontal, using
772             * h*2 would be a good way to count the pixels in those sections.
773             * We then have to figure out the size of the remainder of the
774             * horizontal lines at top and bottom to get the answer:
775             *
776             *     (diagonals less endpoints)*2 + (horizontals)*2
777             *
778             *  or:
779             *
780             *     (h-2)*2 + ((x+w-1-w/4)-(x+w/4)+1)*2
781             *
782             *  since (w == h == size), we then have:
783             *
784             *     (size - size/4 - 1) * 4
785             */
786            int size = ctx.size;
787            if (size <= 1) {
788                return 1;
789            } else {
790                return (size - (size / 4) - 1) * 4;
791            }
792        }
793
794        public void runTest(Object ctx, int numReps) {
795            RenderTests.Context rctx = (RenderTests.Context) ctx;
796            // subtract 1 to account for the fact that lines are drawn to
797            // and including the final coordinate...
798            int size = rctx.size - 1;
799            int x = rctx.initX;
800            int y = rctx.initY;
801            int hexaX[] = new int[6];
802            int hexaY[] = new int[6];
803            Graphics g = rctx.graphics;
804            g.translate(rctx.orgX, rctx.orgY);
805            Color rCArray[] = rctx.colorlist;
806            int ci = rctx.colorindex;
807            do {
808                hexaX[0] = x;
809                hexaX[1] = hexaX[5] = x+size/4;
810                hexaX[2] = hexaX[4] = x+size-size/4;
811                hexaX[3] = x+size;
812                hexaY[1] = hexaY[2] = y;
813                hexaY[0] = hexaY[3] = y+size/2;
814                hexaY[4] = hexaY[5] = y+size;
815
816                if (rCArray != null) {
817                    g.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
818                }
819                g.drawPolygon(hexaX, hexaY, 6);
820                if ((x -= 3) < 0) x += rctx.maxX;
821                if ((y -= 1) < 0) y += rctx.maxY;
822            } while (--numReps > 0);
823            rctx.colorindex = ci;
824            g.translate(-rctx.orgX, -rctx.orgY);
825        }
826    }
827
828    public static class FillCubics extends RenderTests {
829        static final double relTmax = .5 - Math.sqrt(3) / 6;
830        static final double relYmax = ((6*relTmax - 9)*relTmax + 3)*relTmax;
831
832        public FillCubics() {
833            super(rendershaperoot, "fillCubic", "Fill Bezier Curves");
834        }
835
836        public int pixelsTouched(GraphicsTests.Context ctx) {
837            /*
838             * The cubic only touches 2 quadrants in the square, thus
839             * at least half of the square is unfilled.  The integrals
840             * to figure out the exact area are not trivial so for the
841             * other 2 quadrants, I'm going to guess that the cubic only
842             * encloses somewhere between 1/2 and 3/4ths of the pixels
843             * in those quadrants - we will say 5/8ths.  Thus only
844             * 5/16ths of the total square is filled.
845             */
846            // Note: 2x2 ends up hitting exactly 1 pixel...
847            int size = ctx.size;
848            if (size < 2) size = 2;
849            return size * size * 5 / 16;
850        }
851
852        public static class Context extends RenderTests.Context {
853            CubicCurve2D curve = new CubicCurve2D.Float();
854        }
855
856        public GraphicsTests.Context createContext() {
857            return new FillCubics.Context();
858        }
859
860        public void runTest(Object ctx, int numReps) {
861            FillCubics.Context cctx = (FillCubics.Context) ctx;
862            int size = cctx.size;
863            // Note: 2x2 ends up hitting exactly 1 pixel...
864            if (size < 2) size = 2;
865            int x = cctx.initX;
866            int y = cctx.initY;
867            int cpoffset = (int) (size/relYmax/2);
868            CubicCurve2D curve = cctx.curve;
869            Graphics2D g2d = (Graphics2D) cctx.graphics;
870            g2d.translate(cctx.orgX, cctx.orgY);
871            Color rCArray[] = cctx.colorlist;
872            int ci = cctx.colorindex;
873            do {
874                curve.setCurve(x, y+size/2.0,
875                               x+size/2.0, y+size/2.0-cpoffset,
876                               x+size/2.0, y+size/2.0+cpoffset,
877                               x+size, y+size/2.0);
878
879                if (rCArray != null) {
880                    g2d.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
881                }
882                g2d.fill(curve);
883                if ((x -= 3) < 0) x += cctx.maxX;
884                if ((y -= 1) < 0) y += cctx.maxY;
885            } while (--numReps > 0);
886            cctx.colorindex = ci;
887            g2d.translate(-cctx.orgX, -cctx.orgY);
888        }
889    }
890
891    public static class DrawCubics extends RenderTests {
892        static final double relTmax = .5 - Math.sqrt(3) / 6;
893        static final double relYmax = ((6*relTmax - 9)*relTmax + 3)*relTmax;
894
895        public DrawCubics() {
896            super(rendershaperoot, "drawCubic", "Draw Bezier Curves");
897        }
898
899        public int pixelsTouched(GraphicsTests.Context ctx) {
900            // Gross approximation
901            int size = ctx.size;
902            if (size < 2) size = 2;
903            return size;
904        }
905
906        public static class Context extends RenderTests.Context {
907            CubicCurve2D curve = new CubicCurve2D.Float();
908        }
909
910        public GraphicsTests.Context createContext() {
911            return new DrawCubics.Context();
912        }
913
914        public void runTest(Object ctx, int numReps) {
915            DrawCubics.Context cctx = (DrawCubics.Context) ctx;
916            int size = cctx.size;
917            // Note: 2x2 ends up hitting exactly 1 pixel...
918            if (size < 2) size = 2;
919            int x = cctx.initX;
920            int y = cctx.initY;
921            int cpoffset = (int) (size/relYmax/2);
922            CubicCurve2D curve = cctx.curve;
923            Graphics2D g2d = (Graphics2D) cctx.graphics;
924            g2d.translate(cctx.orgX, cctx.orgY);
925            Color rCArray[] = cctx.colorlist;
926            int ci = cctx.colorindex;
927            do {
928                curve.setCurve(x, y+size/2.0,
929                               x+size/2.0, y+size/2.0-cpoffset,
930                               x+size/2.0, y+size/2.0+cpoffset,
931                               x+size, y+size/2.0);
932
933                if (rCArray != null) {
934                    g2d.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
935                }
936                g2d.draw(curve);
937                if ((x -= 3) < 0) x += cctx.maxX;
938                if ((y -= 1) < 0) y += cctx.maxY;
939            } while (--numReps > 0);
940            cctx.colorindex = ci;
941            g2d.translate(-cctx.orgX, -cctx.orgY);
942        }
943    }
944
945    public static class FillEllipse2Ds extends RenderTests {
946        public FillEllipse2Ds() {
947            super(rendershaperoot, "fillEllipse2D", "Fill Ellipse2Ds");
948        }
949
950        public int pixelsTouched(GraphicsTests.Context ctx) {
951            // Approximated (copied from FillOvals.pixelsTouched())
952            double xaxis = ctx.outdim.width / 2.0;
953            double yaxis = ctx.outdim.height / 2.0;
954            return (int) (xaxis * yaxis * Math.PI);
955        }
956
957        public static class Context extends RenderTests.Context {
958            Ellipse2D ellipse = new Ellipse2D.Float();
959        }
960
961        public GraphicsTests.Context createContext() {
962            return new FillEllipse2Ds.Context();
963        }
964
965        public void runTest(Object ctx, int numReps) {
966            FillEllipse2Ds.Context cctx = (FillEllipse2Ds.Context) ctx;
967            int size = cctx.size;
968            int x = cctx.initX;
969            int y = cctx.initY;
970            Ellipse2D ellipse = cctx.ellipse;
971            Graphics2D g2d = (Graphics2D) cctx.graphics;
972            g2d.translate(cctx.orgX, cctx.orgY);
973            Color rCArray[] = cctx.colorlist;
974            int ci = cctx.colorindex;
975            do {
976                if (rCArray != null) {
977                    g2d.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
978                }
979                ellipse.setFrame(x, y, size, size);
980                g2d.fill(ellipse);
981                if ((x -= 3) < 0) x += cctx.maxX;
982                if ((y -= 1) < 0) y += cctx.maxY;
983            } while (--numReps > 0);
984            cctx.colorindex = ci;
985            g2d.translate(-cctx.orgX, -cctx.orgY);
986        }
987    }
988
989    public static class DrawEllipse2Ds extends RenderTests {
990        public DrawEllipse2Ds() {
991            super(rendershaperoot, "drawEllipse2D", "Draw Ellipse2Ds");
992        }
993
994        public int pixelsTouched(GraphicsTests.Context ctx) {
995            // Approximated (copied from DrawOvals.pixelsTouched())
996            return (int)(Math.sqrt(2.0)*(ctx.outdim.width+ctx.outdim.height));
997        }
998
999        public static class Context extends RenderTests.Context {
1000            Ellipse2D ellipse = new Ellipse2D.Float();
1001        }
1002
1003        public GraphicsTests.Context createContext() {
1004            return new DrawEllipse2Ds.Context();
1005        }
1006
1007        public void runTest(Object ctx, int numReps) {
1008            DrawEllipse2Ds.Context cctx = (DrawEllipse2Ds.Context) ctx;
1009            int size = cctx.size;
1010            int x = cctx.initX;
1011            int y = cctx.initY;
1012            Ellipse2D ellipse = cctx.ellipse;
1013            Graphics2D g2d = (Graphics2D) cctx.graphics;
1014            g2d.translate(cctx.orgX, cctx.orgY);
1015            Color rCArray[] = cctx.colorlist;
1016            int ci = cctx.colorindex;
1017            do {
1018                if (rCArray != null) {
1019                    g2d.setColor(rCArray[ci++ & NUM_RANDOMCOLORMASK]);
1020                }
1021                ellipse.setFrame(x, y, size, size);
1022                g2d.draw(ellipse);
1023                if ((x -= 3) < 0) x += cctx.maxX;
1024                if ((y -= 1) < 0) y += cctx.maxY;
1025            } while (--numReps > 0);
1026            cctx.colorindex = ci;
1027            g2d.translate(-cctx.orgX, -cctx.orgY);
1028        }
1029    }
1030}
1031