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