1 /*
   2  * Copyright (c) 2002, 2014, 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 
  41 package j2dbench.tests;
  42 
  43 import java.awt.Graphics;
  44 import java.awt.Graphics2D;
  45 import java.awt.AlphaComposite;
  46 import java.awt.RenderingHints;
  47 import java.awt.Polygon;
  48 import java.awt.Color;
  49 import java.awt.Dimension;
  50 import java.awt.geom.Point2D;
  51 import java.awt.geom.AffineTransform;
  52 import java.lang.reflect.Field;
  53 
  54 import j2dbench.Destinations;
  55 import j2dbench.Group;
  56 import j2dbench.Option;
  57 import j2dbench.Result;
  58 import j2dbench.Test;
  59 import j2dbench.TestEnvironment;
  60 
  61 public abstract class GraphicsTests extends Test {
  62     public static boolean hasGraphics2D;
  63 
  64     static {
  65         try {
  66             hasGraphics2D = (Graphics2D.class != null);
  67         } catch (NoClassDefFoundError e) {
  68         }
  69     }
  70 
  71     static Color makeAlphaColor(Color opaque, int alpha) {
  72         try {
  73             opaque = new Color(opaque.getRed(),
  74                                opaque.getGreen(),
  75                                opaque.getBlue(),
  76                                alpha);
  77         } catch (NoSuchMethodError e) {
  78         }
  79         return opaque;
  80     }
  81 
  82     static Group graphicsroot;
  83     static Group groptroot;
  84 
  85     static Option animList;
  86     static Option sizeList;
  87     static Option compRules;
  88     static Option transforms;
  89     static Option doExtraAlpha;
  90     static Option doXor;
  91     static Option doClipping;
  92     static Option renderHint;
  93     // REMIND: transform, etc.
  94 
  95     public static void init() {
  96         graphicsroot = new Group("graphics", "Graphics Benchmarks");
  97         graphicsroot.setTabbed();
  98 
  99         groptroot = new Group(graphicsroot, "opts", "General Graphics Options");
 100 
 101         animList = new Option.IntList(groptroot, "anim",
 102                                       "Movement of rendering position",
 103                                       new int[] {0, 1, 2},
 104                                       new String[] {
 105                                           "static", "slide", "bounce",
 106                                       },
 107                                       new String[] {
 108                                           "No movement",
 109                                           "Shift horizontal alignment",
 110                                           "Bounce around window",
 111                                       }, 0x4);
 112 
 113         sizeList = new Option.IntList(groptroot, "sizes",
 114                                       "Size of Operations to perform",
 115                                       new int[] {1, 20, 100, 250, 1000, 4000},
 116                                       new String[] {
 117                                           "1x1", "20x20", "100x100", "250x250",
 118                                           "1000x1000", "4000x4000",
 119                                       },
 120                                       new String[] {
 121                                           "Tiny Shapes (1x1)",
 122                                           "Small Shapes (20x20)",
 123                                           "Medium Shapes (100x100)",
 124                                           "Large Shapes (250x250)",
 125                                           "X-Large Shapes (1000x1000)",
 126                                           "Huge Shapes (4000x4000)",
 127                                       }, 0xa);
 128         if (hasGraphics2D) {
 129             String rulenames[] = {
 130                 "Clear",
 131                 "Src",
 132                 "Dst",
 133                 "SrcOver",
 134                 "DstOver",
 135                 "SrcIn",
 136                 "DstIn",
 137                 "SrcOut",
 138                 "DstOut",
 139                 "SrcAtop",
 140                 "DstAtop",
 141                 "Xor",
 142             };
 143             String ruledescs[] = new String[rulenames.length];
 144             Object rules[] = new Object[rulenames.length];
 145             int j = 0;
 146             int defrule = 0;
 147             for (int i = 0; i < rulenames.length; i++) {
 148                 String rulename = rulenames[i];
 149                 try {
 150                     Field f = AlphaComposite.class.getField(rulename);
 151                     rules[j] = f.get(null);
 152                 } catch (NoSuchFieldException nsfe) {
 153                     continue;
 154                 } catch (IllegalAccessException iae) {
 155                     continue;
 156                 }
 157                 if (rules[j] == AlphaComposite.SrcOver) {
 158                     defrule = j;
 159                 }
 160                 rulenames[j] = rulename;
 161                 String suffix;
 162                 if (rulename.startsWith("Src")) {
 163                     suffix = rulename.substring(3);
 164                     rulename = "Source";
 165                 } else if (rulename.startsWith("Dst")) {
 166                     suffix = rulename.substring(3);
 167                     rulename = "Dest";
 168                 } else {
 169                     suffix = "";
 170                 }
 171                 if (suffix.length() > 0) {
 172                     suffix = " "+suffix;
 173                 }
 174                 ruledescs[j] = rulename+suffix;
 175                 j++;
 176             }
 177             compRules =
 178                 new Option.ObjectList(groptroot, "alpharule",
 179                                       "AlphaComposite Rule",
 180                                       j, rulenames, rules, rulenames,
 181                                       ruledescs, (1 << defrule));
 182             ((Option.ObjectList) compRules).setNumRows(4);
 183 
 184             Transform xforms[] = {
 185                 Identity.instance,
 186                 FTranslate.instance,
 187                 Scale2x2.instance,
 188                 Rotate15.instance,
 189                 ShearX.instance,
 190                 ShearY.instance,
 191             };
 192             String xformnames[] = new String[xforms.length];
 193             String xformdescs[] = new String[xforms.length];
 194             for (int i = 0; i < xforms.length; i++) {
 195                 xformnames[i] = xforms[i].getShortName();
 196                 xformdescs[i] = xforms[i].getDescription();
 197             }
 198             transforms =
 199                 new Option.ObjectList(groptroot, "transform",
 200                                       "Affine Transform",
 201                                       xforms.length,
 202                                       xformnames, xforms, xformnames,
 203                                       xformdescs, 0x1);
 204             ((Option.ObjectList) transforms).setNumRows(3);
 205 
 206             doExtraAlpha =
 207                 new Option.Toggle(groptroot, "extraalpha",
 208                                   "Render with an \"extra alpha\" of 0.125",
 209                                   Option.Toggle.Off);
 210             doXor =
 211                 new Option.Toggle(groptroot, "xormode",
 212                                   "Render in XOR mode", Option.Toggle.Off);
 213             doClipping =
 214                 new Option.Toggle(groptroot, "clip",
 215                                   "Render through a complex clip shape",
 216                                   Option.Toggle.Off);
 217             String rhintnames[] = {
 218                 "Default", "Speed", "Quality",
 219             };
 220             renderHint =
 221                 new Option.ObjectList(groptroot, "renderhint",
 222                                       "Rendering Hint",
 223                                       rhintnames, new Object[] {
 224                                           RenderingHints.VALUE_RENDER_DEFAULT,
 225                                           RenderingHints.VALUE_RENDER_SPEED,
 226                                           RenderingHints.VALUE_RENDER_QUALITY,
 227                                       }, rhintnames, rhintnames, 1);
 228         }
 229     }
 230 
 231     public static class Context {
 232         Graphics graphics;
 233         Dimension outdim;
 234         boolean animate;
 235         int size;
 236         int orgX, orgY;
 237         int initX, initY;
 238         int maxX, maxY;
 239         double pixscale;
 240     }
 241 
 242     public GraphicsTests(Group parent, String nodeName, String description) {
 243         super(parent, nodeName, description);
 244         addDependency(Destinations.destroot);
 245         addDependencies(groptroot, false);
 246     }
 247 
 248     public Object initTest(TestEnvironment env, Result result) {
 249         Context ctx = createContext();
 250         initContext(env, ctx);
 251         result.setUnits((int) (ctx.pixscale * pixelsTouched(ctx)));
 252         result.setUnitName("pixel");
 253         return ctx;
 254     }
 255 
 256     public int pixelsTouched(Context ctx) {
 257         return ctx.outdim.width * ctx.outdim.height;
 258     }
 259 
 260     public Context createContext() {
 261         return new Context();
 262     }
 263 
 264     public Dimension getOutputSize(int w, int h) {
 265         return new Dimension(w, h);
 266     }
 267 
 268     public void initContext(TestEnvironment env, Context ctx) {
 269         ctx.graphics = env.getGraphics();
 270         int w = env.getWidth();
 271         int h = env.getHeight();
 272         ctx.size = env.getIntValue(sizeList);
 273         ctx.outdim = getOutputSize(ctx.size, ctx.size);
 274         ctx.pixscale = 1.0;
 275         if (hasGraphics2D) {
 276             Graphics2D g2d = (Graphics2D) ctx.graphics;
 277             AlphaComposite ac = (AlphaComposite) env.getModifier(compRules);
 278             if (env.isEnabled(doExtraAlpha)) {
 279                 ac = AlphaComposite.getInstance(ac.getRule(), 0.125f);
 280             }
 281             g2d.setComposite(ac);
 282             if (env.isEnabled(doXor)) {
 283                 g2d.setXORMode(Color.white);
 284             }
 285             if (env.isEnabled(doClipping)) {
 286                 Polygon p = new Polygon();
 287                 p.addPoint(0, 0);
 288                 p.addPoint(w, 0);
 289                 p.addPoint(0, h);
 290                 p.addPoint(w, h);
 291                 p.addPoint(0, 0);
 292                 g2d.clip(p);
 293             }
 294             Transform tx = (Transform) env.getModifier(transforms);
 295             Dimension envdim = new Dimension(w, h);
 296             tx.init(g2d, ctx, envdim);
 297             w = envdim.width;
 298             h = envdim.height;
 299             g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
 300                                  env.getModifier(renderHint));
 301         }
 302         switch (env.getIntValue(animList)) {
 303         case 0:
 304             ctx.animate = false;
 305             ctx.maxX = 3;
 306             ctx.maxY = 1;
 307             ctx.orgX = (w - ctx.outdim.width) / 2;
 308             ctx.orgY = (h - ctx.outdim.height) / 2;
 309             break;
 310         case 1:
 311             ctx.animate = true;
 312             ctx.maxX = Math.max(Math.min(32, w - ctx.outdim.width), 3);
 313             ctx.maxY = 1;
 314             ctx.orgX = (w - ctx.outdim.width - ctx.maxX) / 2;
 315             ctx.orgY = (h - ctx.outdim.height) / 2;
 316             break;
 317         case 2:
 318             ctx.animate = true;
 319             ctx.maxX = (w - ctx.outdim.width) + 1;
 320             ctx.maxY = (h - ctx.outdim.height) + 1;
 321             ctx.maxX = adjustWidth(ctx.maxX, ctx.maxY);
 322             ctx.maxX = Math.max(ctx.maxX, 3);
 323             ctx.maxY = Math.max(ctx.maxY, 1);
 324             // ctx.orgX = ctx.orgY = 0;
 325             break;
 326         }
 327         ctx.initX = ctx.maxX / 2;
 328         ctx.initY = ctx.maxY / 2;
 329     }
 330 
 331     public void cleanupTest(TestEnvironment env, Object ctx) {
 332         Graphics graphics = ((Context) ctx).graphics;
 333         graphics.dispose();
 334         ((Context) ctx).graphics = null;
 335     }
 336 
 337     public abstract static class Transform {
 338         public abstract String getShortName();
 339         public abstract String getDescription();
 340         public abstract void init(Graphics2D g2d, Context ctx, Dimension dim);
 341 
 342         public static double scaleForPoint(AffineTransform at,
 343                                            double xorig, double yorig,
 344                                            double x, double y,
 345                                            int w, int h)
 346         {
 347             Point2D.Double ptd = new Point2D.Double(x, y);
 348             at.transform(ptd, ptd);
 349             x = ptd.getX();
 350             y = ptd.getY();
 351             double scale = 1.0;
 352             if (x < 0) {
 353                 scale = Math.min(scale, xorig / (xorig - x));
 354             } else if (x > w) {
 355                 scale = Math.min(scale, (w - xorig) / (x - xorig));
 356             }
 357             if (y < 0) {
 358                 scale = Math.min(scale, yorig / (yorig - y));
 359             } else if (y > h) {
 360                 scale = Math.min(scale, (h - yorig) / (y - yorig));
 361             }
 362             return scale;
 363         }
 364 
 365         public static Dimension scaleForTransform(AffineTransform at,
 366                                                   Dimension dim)
 367         {
 368             int w = dim.width;
 369             int h = dim.height;
 370             Point2D.Double ptd = new Point2D.Double(0, 0);
 371             at.transform(ptd, ptd);
 372             double ox = ptd.getX();
 373             double oy = ptd.getY();
 374             if (ox < 0 || ox > w || oy < 0 || oy > h) {
 375                 throw new InternalError("origin outside destination");
 376             }
 377             double scalex = scaleForPoint(at, ox, oy, w, h, w, h);
 378             double scaley = scalex;
 379             scalex = Math.min(scaleForPoint(at, ox, oy, w, 0, w, h), scalex);
 380             scaley = Math.min(scaleForPoint(at, ox, oy, 0, h, w, h), scaley);
 381             if (scalex < 0 || scaley < 0) {
 382                 throw new InternalError("could not fit dims to transform");
 383             }
 384             return new Dimension((int) Math.floor(w * scalex),
 385                                  (int) Math.floor(h * scaley));
 386         }
 387     }
 388 
 389     public static class Identity extends Transform {
 390         public static final Identity instance = new Identity();
 391 
 392         private Identity() {}
 393 
 394         public String getShortName() {
 395             return "ident";
 396         }
 397 
 398         public String getDescription() {
 399             return "Identity";
 400         }
 401 
 402         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 403         }
 404     }
 405 
 406     public static class FTranslate extends Transform {
 407         public static final FTranslate instance = new FTranslate();
 408 
 409         private FTranslate() {}
 410 
 411         public String getShortName() {
 412             return "ftrans";
 413         }
 414 
 415         public String getDescription() {
 416             return "FTranslate 1.5";
 417         }
 418 
 419         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 420             int w = dim.width;
 421             int h = dim.height;
 422             AffineTransform at = new AffineTransform();
 423             at.translate(1.5, 1.5);
 424             g2d.transform(at);
 425             dim.setSize(w-3, h-3);
 426         }
 427     }
 428 
 429     public static class Scale2x2 extends Transform {
 430         public static final Scale2x2 instance = new Scale2x2();
 431 
 432         private Scale2x2() {}
 433 
 434         public String getShortName() {
 435             return "scale2x2";
 436         }
 437 
 438         public String getDescription() {
 439             return "Scale 2x by 2x";
 440         }
 441 
 442         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 443             int w = dim.width;
 444             int h = dim.height;
 445             AffineTransform at = new AffineTransform();
 446             at.scale(2.0, 2.0);
 447             g2d.transform(at);
 448             dim.setSize(w/2, h/2);
 449             ctx.pixscale = 4;
 450         }
 451     }
 452 
 453     public static class Rotate15 extends Transform {
 454         public static final Rotate15 instance = new Rotate15();
 455 
 456         private Rotate15() {}
 457 
 458         public String getShortName() {
 459             return "rot15";
 460         }
 461 
 462         public String getDescription() {
 463             return "Rotate 15 degrees";
 464         }
 465 
 466         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 467             int w = dim.width;
 468             int h = dim.height;
 469             double theta = Math.toRadians(15);
 470             double cos = Math.cos(theta);
 471             double sin = Math.sin(theta);
 472             double xsize = sin * h + cos * w;
 473             double ysize = sin * w + cos * h;
 474             double scale = Math.min(w / xsize, h / ysize);
 475             xsize *= scale;
 476             ysize *= scale;
 477             AffineTransform at = new AffineTransform();
 478             at.translate((w - xsize) / 2.0, (h - ysize) / 2.0);
 479             at.translate(sin * h * scale, 0.0);
 480             at.rotate(theta);
 481             g2d.transform(at);
 482             dim.setSize(scaleForTransform(at, dim));
 483         }
 484     }
 485 
 486     public static class ShearX extends Transform {
 487         public static final ShearX instance = new ShearX();
 488 
 489         private ShearX() {}
 490 
 491         public String getShortName() {
 492             return "shearx";
 493         }
 494 
 495         public String getDescription() {
 496             return "Shear X to the right";
 497         }
 498 
 499         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 500             int w = dim.width;
 501             int h = dim.height;
 502             AffineTransform at = new AffineTransform();
 503             at.translate(0.0, (h - (w*h)/(w + h*0.1)) / 2);
 504             at.shear(0.1, 0.0);
 505             g2d.transform(at);
 506             dim.setSize(scaleForTransform(at, dim));
 507         }
 508     }
 509 
 510     public static class ShearY extends Transform {
 511         public static final ShearY instance = new ShearY();
 512 
 513         private ShearY() {}
 514 
 515         public String getShortName() {
 516             return "sheary";
 517         }
 518 
 519         public String getDescription() {
 520             return "Shear Y down";
 521         }
 522 
 523         public void init(Graphics2D g2d, Context ctx, Dimension dim) {
 524             int w = dim.width;
 525             int h = dim.height;
 526             AffineTransform at = new AffineTransform();
 527             at.translate((w - (w*h)/(h + w*0.1)) / 2, 0.0);
 528             at.shear(0.0, 0.1);
 529             g2d.transform(at);
 530             dim.setSize(scaleForTransform(at, dim));
 531         }
 532     }
 533 }