1 /*
   2  * Copyright (c) 2013, 2014, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.com.sun.javafx.sg.prism;
  27 
  28 import javafx.geometry.Insets;
  29 import javafx.scene.layout.Background;
  30 import javafx.scene.layout.BackgroundFill;
  31 import javafx.scene.layout.CornerRadii;
  32 import java.lang.reflect.Field;
  33 import com.sun.javafx.geom.BaseBounds;
  34 import com.sun.javafx.geom.DirtyRegionContainer;
  35 import com.sun.javafx.geom.DirtyRegionPool;
  36 import com.sun.javafx.geom.RectBounds;
  37 import com.sun.javafx.geom.transform.BaseTransform;
  38 import com.sun.javafx.geom.transform.GeneralTransform3D;
  39 import com.sun.javafx.sg.prism.NGCircleShim;
  40 import com.sun.javafx.sg.prism.NGGroupShim;
  41 import com.sun.javafx.sg.prism.NGNode;
  42 import com.sun.javafx.sg.prism.NGNodeShim;
  43 import com.sun.javafx.sg.prism.NGRectangleShim;
  44 import com.sun.javafx.sg.prism.NGRegionShim;
  45 import com.sun.javafx.sg.prism.NodeEffectInput;
  46 import com.sun.prism.Graphics;
  47 import com.sun.prism.paint.Color;
  48 import com.sun.scenario.effect.Effect;
  49 
  50 /**
  51  * Base class for all tests in this package. The tests in this package are all about
  52  * testing the NGNode and associated classes. These tests all need to be able to work
  53  * directly against the NGNodes and not rely on the upper level scene graph nodes.
  54  * However Node and the other classes do manage a lot of tasks (like setting up the bounds
  55  * and transforms appropriately) during synchronization. This class contains all of the
  56  * useful convenience methods for creating scene graphs of NG nodes and manipulating them
  57  * so that we can test the NG nodes.
  58  */
  59 public class NGTestBase {
  60 
  61     /** Transforms the given node by the specified transform. */
  62     protected static <N extends NGNode> void transform(N node, BaseTransform tx) {
  63         // Concatenate this transform with the one already on the node
  64         tx = node.getTransform().copy().deriveWithConcatenation(tx);
  65         // Compute & set the new transformed bounds for the node
  66         node.setTransformedBounds(node.getEffectBounds(new RectBounds(), tx), false);
  67         // Set the transform matrix
  68         node.setTransformMatrix(tx);
  69     }
  70 
  71     /** Translate the given node by the specified amount */
  72     protected static <N extends NGNode> void translate(N node, double tx, double ty) {
  73         transform(node, BaseTransform.getTranslateInstance(tx, ty));
  74     }
  75 
  76     /** Set the given effect on the node. effect must not be null. */
  77     protected static <N extends NGNode> void setEffect(N node, Effect effect) {
  78         node.setEffect(null); // so that when we ask for the getEffectBounds, it won't include an old effect
  79         BaseBounds effectBounds = new RectBounds();
  80         effectBounds = effectBounds.deriveWithNewBounds(effect.getBounds(BaseTransform.IDENTITY_TRANSFORM, new NodeEffectInput(node)));
  81         BaseBounds clippedBounds = node.getEffectBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
  82         node.setEffect(effect);
  83         // The new transformed bounds should be the union of the old effect bounds, new effect bounds, and
  84         // then transform those bounds. The reason I'm doing it this way is to expose any bugs in the
  85         // getEffectBounds() implementation when an effect is present.
  86         effectBounds = effectBounds.deriveWithUnion(clippedBounds);
  87         node.setTransformedBounds(node.getTransform().transform(effectBounds, effectBounds), false);
  88     }
  89 
  90     public static TestNGRectangle createRectangle(int x, int y, int width, int height) {
  91         TestNGRectangle rect = new TestNGRectangle();
  92         rect.updateRectangle(x, y, width, height, 0, 0);
  93         final RectBounds bounds = new RectBounds(x, y, x + width, y + height);
  94         rect.setContentBounds(bounds);
  95         rect.setFillPaint(new Color(0, 0, 0, 1.0f));
  96         rect.setTransformMatrix(BaseTransform.IDENTITY_TRANSFORM);
  97         rect.setTransformedBounds(bounds, false);
  98         return rect;
  99     }
 100 
 101     public static TestNGCircle createCircle(int cx, int cy, int radius) {
 102         TestNGCircle c = new TestNGCircle();
 103         c.updateCircle(cx, cy, radius);
 104         final RectBounds bounds = new RectBounds(cx - radius, cy - radius, cx + radius, cy + radius);
 105         c.setContentBounds(bounds);
 106         c.setFillPaint(new Color(0, 0, 0, 1.0f));
 107         c.setTransformMatrix(BaseTransform.IDENTITY_TRANSFORM);
 108         c.setTransformedBounds(bounds, false);
 109         return c;
 110     }
 111 
 112     public static TestNGRegion createOpaqueRegion(int x, int y, int width, int height, NGNode... children) {
 113         TestNGRegion r = createTransparentRegion(x, y, width, height, children);
 114         r.updateBackground(new Background(new BackgroundFill(javafx.scene.paint.Color.BLACK, null, null)));
 115         r.setOpaqueInsets(0, 0, 0, 0);
 116         return r;
 117     }
 118 
 119     public static TestNGRegion createTransparentRegion(int x, int y, int width, int height, NGNode... children) {
 120         TestNGRegion r = new TestNGRegion();
 121         for (NGNode child : children) {
 122             r.add(-1, child);
 123         }
 124         r.setSize(width, height);
 125         final RectBounds bounds = new RectBounds(0, 0, width, height);
 126         r.setContentBounds(bounds);
 127         if (x != 0 || y != 0) {
 128             r.setTransformMatrix(BaseTransform.getTranslateInstance(x, y));
 129             r.setTransformedBounds(r.getTransform().transform(bounds, new RectBounds()), true);
 130         } else {
 131             r.setTransformMatrix(BaseTransform.IDENTITY_TRANSFORM);
 132             r.setTransformedBounds(bounds, false);
 133         }
 134         return r;
 135     }
 136 
 137     public static TestNGGroup createGroup(NGNode... children) {
 138         TestNGGroup group = new TestNGGroup();
 139         BaseBounds contentBounds = new RectBounds();
 140         for (NGNode child : children) {
 141             contentBounds = contentBounds.deriveWithUnion(
 142                     child.getCompleteBounds(
 143                             new RectBounds(), BaseTransform.IDENTITY_TRANSFORM));
 144             group.add(-1, child);
 145         }
 146         group.setContentBounds(contentBounds);
 147         group.setTransformMatrix(BaseTransform.IDENTITY_TRANSFORM);
 148         group.setTransformedBounds(contentBounds, false);
 149         return group;
 150     }
 151 
 152     public static TestNGRegion createRegion(int w, int h, NGNode... children) {
 153         TestNGRegion region = new TestNGRegion();
 154         BaseBounds contentBounds = new RectBounds();
 155         for (NGNode child : children) {
 156             contentBounds = contentBounds.deriveWithUnion(
 157                     child.getCompleteBounds(
 158                             new RectBounds(), BaseTransform.IDENTITY_TRANSFORM));
 159             region.add(-1, child);
 160         }
 161         region.setContentBounds(contentBounds);
 162         region.setTransformMatrix(BaseTransform.IDENTITY_TRANSFORM);
 163         region.setTransformedBounds(contentBounds, false);
 164         region.setSize(w, h);
 165 
 166         // I have to do this nasty reflection trickery because we don't have a Toolkit for creating
 167         // the Prism Color that is the platform peer.
 168         javafx.scene.paint.Color color = new javafx.scene.paint.Color(0, 0, 0, 1);
 169         try {
 170             Field platformPaint = color.getClass().getDeclaredField("platformPaint");
 171             platformPaint.setAccessible(true);
 172             platformPaint.set(color, new Color(0f, 0f, 0f, 1f));
 173         } catch (Exception e) {
 174             e.printStackTrace();
 175         }
 176         Background background = new Background(new BackgroundFill[] {
 177                 new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY)}, null);
 178         region.updateBackground(background);
 179         region.setOpaqueInsets(0, 0, 0, 0);
 180         return region;
 181     }
 182 
 183 
 184     public static final class TestNGGroup extends NGGroupShim implements TestNGNode {
 185         private boolean askedToAccumulateDirtyRegion;
 186         private boolean computedDirtyRegion;
 187         private boolean rendered;
 188 
 189         @Override
 190         protected void renderContent(Graphics g) {
 191             super.renderContent(g);
 192             rendered = true;
 193         }
 194 
 195         @Override public int accumulateDirtyRegions(final RectBounds clip,
 196                 RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx) {
 197             askedToAccumulateDirtyRegion = true;
 198             return super.accumulateDirtyRegions(clip, dirtyRegion, pool, drc, tx, pvTx);
 199         }
 200         @Override public int accumulateGroupDirtyRegion(
 201                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 202         {
 203             computedDirtyRegion = true;
 204             return super.accumulateGroupDirtyRegion(clip, dirtyRegion, pool, drc, tx, pvTx);
 205         }
 206         @Override public int accumulateNodeDirtyRegion(
 207                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 208         {
 209             computedDirtyRegion = true;
 210             return super.accumulateNodeDirtyRegion(clip, dirtyRegion, drc, tx, pvTx);
 211         }
 212         @Override public boolean askedToAccumulateDirtyRegion() { return askedToAccumulateDirtyRegion; }
 213         @Override public boolean computedDirtyRegion() { return computedDirtyRegion; }
 214         @Override public boolean rendered() { return rendered; }
 215     }
 216 
 217     public static final class TestNGRegion extends NGRegionShim implements TestNGNode {
 218         private boolean askedToAccumulateDirtyRegion;
 219         private boolean computedDirtyRegion;
 220         private boolean rendered;
 221 
 222         @Override
 223         protected void renderContent(Graphics g) {
 224             super.renderContent(g);
 225             rendered = true;
 226         }
 227 
 228         @Override public int accumulateDirtyRegions(final RectBounds clip,
 229                 RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx) {
 230             askedToAccumulateDirtyRegion = true;
 231             return super.accumulateDirtyRegions(clip, dirtyRegion, pool, drc, tx, pvTx);
 232         }
 233         @Override public int accumulateGroupDirtyRegion(
 234                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 235         {
 236             computedDirtyRegion = true;
 237             return super.accumulateGroupDirtyRegion(clip, dirtyRegion, pool, drc, tx, pvTx);
 238         }
 239         @Override public int accumulateNodeDirtyRegion(
 240                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 241         {
 242             computedDirtyRegion = true;
 243             return super.accumulateNodeDirtyRegion(clip, dirtyRegion, drc, tx, pvTx);
 244         }
 245         @Override public boolean askedToAccumulateDirtyRegion() { return askedToAccumulateDirtyRegion; }
 246         @Override public boolean computedDirtyRegion() { return computedDirtyRegion; }
 247         @Override public boolean rendered() { return rendered; }
 248     }
 249 
 250     public static final class TestNGRectangle extends NGRectangleShim implements TestNGNode {
 251         private boolean askedToAccumulateDirtyRegion;
 252         private boolean computedDirtyRegion;
 253         private boolean rendered;
 254 
 255         @Override
 256         protected void renderContent(Graphics g) {
 257             rendered = true;
 258         }
 259 
 260         @Override public int accumulateDirtyRegions(final RectBounds clip,
 261                 RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx) {
 262             askedToAccumulateDirtyRegion = true;
 263             return super.accumulateDirtyRegions(clip, dirtyRegion, pool, drc, tx, pvTx);
 264         }
 265         @Override public int accumulateNodeDirtyRegion(
 266                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 267         {
 268             computedDirtyRegion = true;
 269             return super.accumulateNodeDirtyRegion(clip, dirtyRegion, drc, tx, pvTx);
 270         }
 271         @Override public boolean askedToAccumulateDirtyRegion() { return askedToAccumulateDirtyRegion; }
 272         @Override public boolean computedDirtyRegion() { return computedDirtyRegion; }
 273         @Override public boolean rendered() { return rendered; }
 274     }
 275 
 276     public  static final class TestNGCircle extends NGCircleShim implements TestNGNode {
 277         private boolean askedToAccumulateDirtyRegion;
 278         private boolean computedDirtyRegion;
 279         private boolean rendered;
 280 
 281         @Override
 282         protected void renderContent(Graphics g) {
 283             rendered = true;
 284         }
 285 
 286         @Override public int accumulateDirtyRegions(final RectBounds clip,
 287                 RectBounds dirtyRegion, DirtyRegionPool pool, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx) {
 288             askedToAccumulateDirtyRegion = true;
 289             return super.accumulateDirtyRegions(clip, dirtyRegion, pool, drc, tx, pvTx);
 290         }
 291         @Override public int accumulateNodeDirtyRegion(
 292                 final RectBounds clip, RectBounds dirtyRegion, DirtyRegionContainer drc, BaseTransform tx, GeneralTransform3D pvTx)
 293         {
 294             computedDirtyRegion = true;
 295             return super.accumulateNodeDirtyRegion(clip, dirtyRegion, drc,tx, pvTx);
 296         }
 297         @Override public boolean askedToAccumulateDirtyRegion() { return askedToAccumulateDirtyRegion; }
 298         @Override public boolean computedDirtyRegion() { return computedDirtyRegion; }
 299         @Override public boolean rendered() { return rendered; }
 300 
 301     }
 302 
 303     public interface TestNGNode {
 304         public boolean askedToAccumulateDirtyRegion();
 305         public boolean computedDirtyRegion();
 306         public boolean rendered();
 307     }
 308 
 309     public static abstract class Creator<N extends NGNode> {
 310         public abstract N create();
 311     }
 312 
 313     public static abstract class Polluter {
 314         public BaseTransform tx = BaseTransform.IDENTITY_TRANSFORM;
 315         public abstract void pollute(NGNode node);
 316         public BaseBounds modifiedBounds(NGNode node) {
 317             return DirtyRegionTestBase.getWhatTransformedBoundsWouldBe(node, tx);
 318         }
 319         public RectBounds polluteAndGetExpectedBounds(NGNode node) {
 320             BaseBounds originalBounds = node.getCompleteBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
 321             BaseBounds modifiedBounds = modifiedBounds(node);
 322             BaseBounds expected = originalBounds.deriveWithUnion(modifiedBounds);
 323             pollute(node);
 324             return (RectBounds)expected;
 325         }
 326     }
 327 }