--- old/modules/graphics/src/test/java/com/sun/javafx/sg/prism/DirtyRegionTestBase.java 2015-09-11 21:24:27.701286953 -0400
+++ /dev/null 2015-09-11 11:06:08.592686920 -0400
@@ -1,474 +0,0 @@
-/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package com.sun.javafx.sg.prism;
-
-import javafx.geometry.Insets;
-import javafx.scene.layout.Background;
-import javafx.scene.layout.BackgroundFill;
-import javafx.scene.layout.CornerRadii;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import com.sun.javafx.geom.BaseBounds;
-import com.sun.javafx.geom.DirtyRegionContainer;
-import com.sun.javafx.geom.DirtyRegionPool;
-import com.sun.javafx.geom.RectBounds;
-import com.sun.javafx.geom.transform.BaseTransform;
-import com.sun.javafx.geom.transform.GeneralTransform3D;
-import com.sun.prism.paint.Color;
-import org.junit.runners.Parameterized;
-import static org.junit.Assert.assertEquals;
-
-/**
- * A base class for all testing of the dirty regions. This class contains
- * some useful infrastructure for testing dirty regions, such as the ability
- * to assert that a dirty region matches the expected region; the ability to
- * manage the state on an NG node appropriately (ensuring that the transform,
- * transformed bounds, effect, etc are all managed correctly); and ensuring
- * that all test are run over a set of common parameters, such as when a
- * Node becomes dirty due to opacity changing, visibility changing, geometry
- * changing, and so forth.
- *
- * The DirtyRegionTestBase is parametrized, using different node types
- * (rectangle, ellipse, group) and different methods for becoming dirty
- * (visibility change, geometry change, etc). The cross product of these
- * forms the parameters for the test. Each test method is called using the
- * combination of a node type & dirty method.
- */
-public class DirtyRegionTestBase extends NGTestBase {
- /**
- * Gets the test parameters to use when running these tests. The parameters
- * are a combination of a Polluter and a Creator. The Creator is used to
- * create the node to be tested (might be a rectangle, or Group, or something
- * more complex), while the Polluter is responsible for making the test node
- * dirty by some means. Since the Polluter knows what it did to make the node
- * dirty, it is also responsible for computing and returning what the expected
- * change to the node's geometry is, such that the test code can create the
- * union and test for the appropriate dirty region for this specific node.
- */
- @Parameterized.Parameters
- public static Collection createParameters() {
- // This polluter will change the opacity of the test node
- final Polluter polluteOpacity = new Polluter() {
- @Override public void pollute(NGNode node) { node.setOpacity(.5f); }
- @Override public String toString() { return "Pollute Opacity"; }
- };
- // This polluter will restore the opacity of the test node. That is,
- // the test node did have 0 opacity and now it is going to be changed
- // to an opacity of 1.
- final Polluter restoreOpacity = new Polluter() {
- @Override public void pollute(NGNode node) {
- // I need to hide the node, and then clean up all the dirty
- // state associated with it, and then make it visible again.
- // This simulates making it invisible, painting, and then
- // making it visible again.
- node.setOpacity(0f);
- NGNode parent = node;
- while(parent.getParent() != null) parent = parent.getParent();
- parent.render(TestGraphics.TEST_GRAPHICS);
- // Now we can go ahead and set the opacity
- node.setOpacity(1f);
- }
- @Override public String toString() { return "Restore Opacity"; }
- };
- // This polluter will change the fill of the node. This only works if
- // the test node is of a shape type (the code which creates the test
- // parameters will make sure to only use polluteFill with creators
- // which are create Shapes)
- final Polluter polluteFill = new Polluter() {
- @Override public void pollute(NGNode node) {
- if (node instanceof NGShape) {
- com.sun.javafx.sg.prism.NGShape shape = (NGShape)node;
- shape.setFillPaint(new Color(.43f, .23f, .66f, 1f));
- } else if (node instanceof NGRegion) {
- NGRegion region = (NGRegion)node;
- javafx.scene.paint.Color color = new javafx.scene.paint.Color(.43f, .23f, .66f, 1f);
- // I have to do this nasty reflection trickery because we don't have a Toolkit for creating
- // the Prism Color that is the platform peer.
- try {
- Field platformPaint = color.getClass().getDeclaredField("platformPaint");
- platformPaint.setAccessible(true);
- platformPaint.set(color, new Color(.43f, .23f, .66f, 1f));
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- region.updateBackground(new Background(new BackgroundFill[] {
- new BackgroundFill(
- color,
- CornerRadii.EMPTY, Insets.EMPTY)
- }));
- } else {
- throw new IllegalArgumentException("I don't know how to make the fill dirty on " + node);
- }
- }
- @Override public String toString() { return "Pollute Fill"; }
- };
- // This polluter will translate the test node in a positive direction
- final Polluter pollutePositiveTranslation = new Polluter() {
- { tx = BaseTransform.getTranslateInstance(50, 50); }
- @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
- @Override public String toString() { return "Pollute Positive Translation"; }
- };
- // This polluter will translate the test node in a negative direction
- final Polluter polluteNegativeTranslation = new Polluter() {
- { tx = BaseTransform.getTranslateInstance(-50, -50); }
- @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
- @Override public String toString() { return "Pollute Negative Translation"; }
- };
- // This polluter will give the test node a scale causing it to get bigger
- final Polluter polluteBiggerScale = new Polluter() {
- { tx = BaseTransform.getScaleInstance(2, 2); }
- @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
- @Override public String toString() { return "Pollute Bigger Scale"; }
- };
- // This polluter will give the test node a scale causing it to get smaller
- final Polluter polluteSmallerScale = new Polluter() {
- { tx = BaseTransform.getScaleInstance(.5, .5); }
- @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
- @Override public String toString() { return "Pollute Smaller Scale"; }
- };
- // This polluter will rotate the node about its center
- final Polluter polluteRotate = new Polluter() {
- @Override public void pollute(NGNode node) {
- BaseBounds effectBounds = node.getEffectBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
- BaseTransform tx = BaseTransform.getRotateInstance(45, effectBounds.getWidth()/2f, effectBounds.getHeight()/2f);
- DirtyRegionTestBase.transform(node, tx);
- }
- @Override public BaseBounds modifiedBounds(NGNode node) {
- BaseBounds effectBounds = node.getEffectBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
- BaseTransform tx = BaseTransform.getRotateInstance(45, effectBounds.getWidth() / 2f, effectBounds.getHeight() / 2f);
- return DirtyRegionTestBase.getWhatTransformedBoundsWouldBe(node, tx);
- }
- @Override public String toString() { return "Pollute Rotate"; }
- };
- // This polluter will make the test node invisible
- final Polluter polluteVisibility = new Polluter() {
- @Override public void pollute(NGNode node) {
- node.setVisible(false);
- }
- @Override public String toString() { return "Pollute Visibility"; }
- };
- // This polluter will make an invisible node visible again
- final Polluter restoreVisibility = new Polluter() {
- @Override public void pollute(NGNode node) {
- // I need to hide the node, and then clean up all the dirty
- // state associated with it, and then make it visible again.
- // This simulates making it invisible, painting, and then
- // making it visible again.
- node.setVisible(false);
- NGNode parent = node;
- while(parent.getParent() != null) parent = parent.getParent();
- parent.render(TestGraphics.TEST_GRAPHICS);
- // Now we can go ahead and set the opacity
- node.setVisible(true);
- }
- @Override public String toString() { return "Restore Visibility"; }
- };
-
- // We will populate this list with the parameters with which we will test.
- // Each Object[] within the params is composed of a Creator and a Polluter.
- List params = new ArrayList();
- // A standard list of polluters which applies to all tests
- List polluters = Arrays.asList(new Polluter[]{
- polluteRotate,
- polluteOpacity,
- restoreOpacity,
- polluteVisibility,
- restoreVisibility,
- polluteSmallerScale,
- polluteNegativeTranslation,
- polluteBiggerScale,
- pollutePositiveTranslation
- });
- // Construct the Creator / Polluter pair for Groups
- for (final Polluter polluter : polluters) {
- params.add(new Object[] {new Creator() {
- @Override public NGNode create() { return createGroup(createRectangle(0, 0, 100, 100)); }
- @Override public String toString() { return "Group with one Rectangle"; }
- }, polluter});
- }
- // Construct the Creator / Polluter pair for Rectangles
- List rectanglePolluters = new ArrayList(polluters);
- rectanglePolluters.add(new Polluter() {
- @Override public void pollute(NGNode node) {
- NGRectangle rect = (NGRectangle)node;
- BaseBounds bounds = rect.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
- rect.updateRectangle(bounds.getMinX(), bounds.getMinY(), 25, 25, 0, 0);
- }
- @Override public String toString() { return "Pollute Rectangle Geometry"; }
- });
- for (final Polluter polluter : rectanglePolluters) {
- params.add(new Object[] {new Creator() {
- @Override public NGNode create() { return createRectangle(0, 0, 100, 100); }
- @Override public String toString() { return "Rectangle"; }
- }, polluter});
- }
- // Construct the Creator / Polluter pair for Circles
- List circlePolluters = new ArrayList(polluters);
- circlePolluters.add(new Polluter() {
- @Override public void pollute(NGNode node) {
- NGCircle c = (NGCircle)node;
- BaseBounds bounds = c.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
- c.updateCircle(
- bounds.getMinX() + (bounds.getWidth()/2f),
- bounds.getMinY() + (bounds.getHeight()/2f),
- 10);
- }
- @Override public String toString() { return "Pollute Circle Geometry"; }
- });
- for (final Polluter polluter : circlePolluters) {
- params.add(new Object[] {new Creator() {
- @Override public NGNode create() { return createCircle(50, 50, 50); }
- @Override public String toString() { return "Circle"; }
- }, polluter});
- }
- // Return the populated params collection
- return params;
- }
-
- /**
- * The test node creator. This is called from within the "setUp" method in each
- * subclass to create the nodes that are going to be tested.
- */
- protected Creator creator;
-
- /**
- * The polluter. Subclasses will use the polluter to make a node dirty at the
- * appropriate time in the test method.
- */
- protected Polluter polluter;
-
- /**
- * The root node. This must be created during the "setUp" method in each sub
- * class. The root is needed for actually accumulating dirty regions.
- */
- protected TestNGGroup root;
-
- /**
- * The clip to use when accumulating dirty regions. By default it is
- * ridiculously large, such that none of the tests will ever bump up
- * against the clip. Subclasses should however implement some tests in
- * which they will set the clip to a specific value, and then test
- * whether accumulating dirty regions takes the clip into account.
- */
- protected RectBounds windowClip = new RectBounds(-100000, -100000, 100000, 10000);
-
- /**
- * Creates a new DirtyRegionTestBase. Each subclass must have an identical
- * constructor which simply passes the creator and polluter to this
- * constructor. These instances are passed to the constructor by JUnit,
- * so sub classes don't need to worry about creating these instances
- * (and in fact must not do so).
- */
- protected DirtyRegionTestBase(Creator creator, Polluter polluter) {
- this.creator = creator;
- this.polluter = polluter;
- }
-
- /**
- * Helper method for asserting that the dirty region of the node indicated
- * (start) matches the dirty region which is expected. This method will
- * invoke the accumulateDirtyRegions method on the start node. Accumulating
- * dirty regions requires a clip to be sent along as well. The windowClip is
- * specified on DirtyRegionTestBase (by default it is ridiculously large)
- * but sub classes can change the clip at any time.
- *
- */
- protected void assertDirtyRegionEquals(NGNode start, RectBounds expected) {
- // TODO The root might have changes to its bounds and we should reset these. (RT-26928)
- //DirtyRegionTestBase.resetGroupBounds(root);
- // Accumulate the dirty region, using the windowClip.
- // TODO if we wanted to, we could also make the device space transform parameterized
- // such that we could test that the dirty region accumulation logic all works
- // correctly even in the presence of a non-identity device space transform
- // (RT-26928)
- DirtyRegionPool pool = new DirtyRegionPool(1);
- DirtyRegionContainer drc = pool.checkOut();
- int status = start.accumulateDirtyRegions(
- windowClip,
- new RectBounds(), pool,
- drc,
- BaseTransform.IDENTITY_TRANSFORM, new GeneralTransform3D());
-
- RectBounds dirtyRegion = drc.getDirtyRegion(0) ;
-
- // The accumulation of dirty regions ends up with a slightly
- // padded dirty region, just to make up for any error when
- // transforming. Its quick and dirty but does the job.
- // Perhaps we could avoid adding the slop in the case where
- // there is no rotation or skew involved, but for now we
- // don't. I don't want to populate all my tests with this
- // assumption though in case it ever changes. So I am going
- // to pad the expected here.
- expected = new RectBounds(
- Math.max(expected.getMinX() - 1, dirtyRegion.getMinX()),
- Math.max(expected.getMinY() - 1, dirtyRegion.getMinY()),
- Math.min(expected.getMaxX() + 1, dirtyRegion.getMaxX()),
- Math.min(expected.getMaxY() + 1, dirtyRegion.getMaxY()));
- // Now make the check, and print useful error information in case it fails.
- assertEquals("creator=" + creator + ", polluter=" + polluter, expected, dirtyRegion);
- }
-
- /**
- * Helper method for asserting that the dirty region of the node indicated
- * (start) contains the windowClip and matches the dirty region which is
- * expected. This method will invoke the accumulateDirtyRegions method
- * on the start node. Accumulating dirty regions requires a clip to be sent
- * along as well. The windowClip is specified on DirtyRegionTestBase
- * (by default it is ridiculously large) but sub classes can change the clip
- * at any time.
- */
- protected void assertContainsClip(NGNode start, RectBounds expectedDirtyRegion, int expectedStatus) {
- DirtyRegionPool pool = new DirtyRegionPool(1);
- DirtyRegionContainer drc = pool.checkOut();
- int status = start.accumulateDirtyRegions(
- windowClip,
- new RectBounds(), pool,
- drc,
- BaseTransform.IDENTITY_TRANSFORM, new GeneralTransform3D());
-
- assertEquals(expectedStatus, status);
-
- if (status == DirtyRegionContainer.DTR_OK) {
- assertDirtyRegionEquals(start, expectedDirtyRegion);
- }
- }
-
- /**
- * Accumulates the dirty region, and checks to make sure the the accumulateDirtyRegion
- * method was only called on the nodes supplied. If any node in the tree (starting with
- * and including root) have had accumulateDirtyRegion called and they are NOT in this
- * list of expected nodes, then the assertion fails.
- *
- * This assertion is used to make sure that various performance optimizations are
- * implemented correctly, such that we are not asking nodes to accumulate dirty regions
- * who have no hope of being in the dirty region (such as children of a clean group).
- *
- * @param nodes A non-null array of nodes.
- */
- protected void assertOnlyTheseNodesAreAskedToAccumulateDirtyRegions(NGNode... nodes) {
- accumulateDirtyRegions();
- Set set = new HashSet(Arrays.asList(nodes));
- assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(root, set);
- }
-
- /**
- * Accumulates the dirty region, and checks to make sure that the dirty region
- * computation methods (accumulateNodeDirtyRegion, computeDirtyRegion,
- * accumulateGroupDirtyRegion) are only called on the nodes supplied. If any
- * node in the tree (starting with and including the root) has had one or more
- * of these methods called and they are NOT in the array of expected nodes, then
- * the assertion fails.
- *
- * This assertion is used to make sure various performance optimizations are
- * implemented correctly, such that even if a node is asked to accumulate its
- * dirty region, it doesn't actually do any work if the node is actually clean.
- *
- * @param nodes A non-null array of nodes.
- */
- protected void assertOnlyTheseNodesAreAskedToComputeDirtyRegions(NGNode... nodes) {
- accumulateDirtyRegions();
- Set set = new HashSet(Arrays.asList(nodes));
- assertOnlyTheseNodesWereAskedToComputeDirtyRegions(root, set);
- }
-
- /**
- * Helper method which will reset the group bounds of the root prior to accumulating
- * dirty regions. The root node might have new bounds due to changes in its children,
- * such as transforms or geometry changes.
- */
- private void accumulateDirtyRegions() {
- DirtyRegionPool pool = new DirtyRegionPool(1);
- DirtyRegionTestBase.resetGroupBounds(root);
- root.accumulateDirtyRegions(
- new RectBounds(0, 0, 800, 600),
- new RectBounds(), pool,
- pool.checkOut(),
- BaseTransform.IDENTITY_TRANSFORM,
- new GeneralTransform3D());
- }
-
- /**
- * Helper which walks down the tree checking to see if accumulateDirtyRegion has been
- * called, and throws an exception if it was not expected.
- */
- private void assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(NGNode start, Set nodes) {
- assertEquals(
- "creator=" + creator + ", polluter=" + polluter,
- nodes.contains(start), ((TestNGNode)start).askedToAccumulateDirtyRegion());
- if (start instanceof NGGroup) {
- for (NGNode child : ((NGGroup)start).getChildren()) {
- assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(child, nodes);
- }
- }
- }
-
- /**
- * Helper which walks down the tree checking to see if any of the methods which actually
- * compute the dirty region have been called, and throws an exception if it was not expected.
- */
- private void assertOnlyTheseNodesWereAskedToComputeDirtyRegions(NGNode start, Set nodes) {
- assertEquals(
- "creator=" + creator + ", polluter=" + polluter,
- nodes.contains(start), ((TestNGNode)start).computedDirtyRegion());
- if (start instanceof NGGroup) {
- for (NGNode child : ((NGGroup)start).getChildren()) {
- assertOnlyTheseNodesWereAskedToComputeDirtyRegions(child, nodes);
- }
- }
- }
-
- static protected void resetGroupBounds(NGGroup group) {
- BaseBounds contentBounds = new RectBounds();
- for (NGNode child : group.getChildren()) {
- contentBounds = contentBounds.deriveWithUnion(
- child.getCompleteBounds(
- new RectBounds(), BaseTransform.IDENTITY_TRANSFORM));
- }
- BaseBounds currentContentBounds = group.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
- if (!contentBounds.equals(currentContentBounds)) {
- System.out.println("CurrentContentBounds=" + currentContentBounds + ", bounds=" + contentBounds);
- group.setContentBounds(contentBounds);
- group.setTransformedBounds(group.getEffectBounds(new RectBounds(), group.getTransform()), false);
- }
- }
-
- /**
- * Sort of a non-applying version of the transform method. This method will
- * compute and return what the transformed bounds of the given node would
- * be if the transform were applied to it.
- */
- static protected BaseBounds getWhatTransformedBoundsWouldBe(NGNode node, BaseTransform tx) {
- BaseTransform existing = BaseTransform.IDENTITY_TRANSFORM.deriveWithNewTransform(node.getTransform());
- tx = existing.deriveWithConcatenation(tx);
- return node.getEffectBounds(new RectBounds(), tx);
- }
-}
--- /dev/null 2015-09-11 11:06:08.592686920 -0400
+++ new/modules/graphics/src/test/java/test/com/sun/javafx/sg/prism/DirtyRegionTestBase.java 2015-09-11 21:24:27.565286954 -0400
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.com.sun.javafx.sg.prism;
+
+import javafx.geometry.Insets;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.CornerRadii;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.DirtyRegionContainer;
+import com.sun.javafx.geom.DirtyRegionPool;
+import com.sun.javafx.geom.RectBounds;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.geom.transform.GeneralTransform3D;
+import com.sun.javafx.sg.prism.NGCircle;
+import com.sun.javafx.sg.prism.NGGroup;
+import com.sun.javafx.sg.prism.NGNode;
+import com.sun.javafx.sg.prism.NGRectangle;
+import com.sun.javafx.sg.prism.NGRegion;
+import com.sun.javafx.sg.prism.NGShape;
+import com.sun.prism.paint.Color;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * A base class for all testing of the dirty regions. This class contains
+ * some useful infrastructure for testing dirty regions, such as the ability
+ * to assert that a dirty region matches the expected region; the ability to
+ * manage the state on an NG node appropriately (ensuring that the transform,
+ * transformed bounds, effect, etc are all managed correctly); and ensuring
+ * that all test are run over a set of common parameters, such as when a
+ * Node becomes dirty due to opacity changing, visibility changing, geometry
+ * changing, and so forth.
+ *
+ * The DirtyRegionTestBase is parametrized, using different node types
+ * (rectangle, ellipse, group) and different methods for becoming dirty
+ * (visibility change, geometry change, etc). The cross product of these
+ * forms the parameters for the test. Each test method is called using the
+ * combination of a node type & dirty method.
+ */
+public class DirtyRegionTestBase extends NGTestBase {
+ /**
+ * Gets the test parameters to use when running these tests. The parameters
+ * are a combination of a Polluter and a Creator. The Creator is used to
+ * create the node to be tested (might be a rectangle, or Group, or something
+ * more complex), while the Polluter is responsible for making the test node
+ * dirty by some means. Since the Polluter knows what it did to make the node
+ * dirty, it is also responsible for computing and returning what the expected
+ * change to the node's geometry is, such that the test code can create the
+ * union and test for the appropriate dirty region for this specific node.
+ */
+ @Parameterized.Parameters
+ public static Collection createParameters() {
+ // This polluter will change the opacity of the test node
+ final Polluter polluteOpacity = new Polluter() {
+ @Override public void pollute(NGNode node) { node.setOpacity(.5f); }
+ @Override public String toString() { return "Pollute Opacity"; }
+ };
+ // This polluter will restore the opacity of the test node. That is,
+ // the test node did have 0 opacity and now it is going to be changed
+ // to an opacity of 1.
+ final Polluter restoreOpacity = new Polluter() {
+ @Override public void pollute(NGNode node) {
+ // I need to hide the node, and then clean up all the dirty
+ // state associated with it, and then make it visible again.
+ // This simulates making it invisible, painting, and then
+ // making it visible again.
+ node.setOpacity(0f);
+ NGNode parent = node;
+ while(parent.getParent() != null) parent = parent.getParent();
+ parent.render(TestGraphics.TEST_GRAPHICS);
+ // Now we can go ahead and set the opacity
+ node.setOpacity(1f);
+ }
+ @Override public String toString() { return "Restore Opacity"; }
+ };
+ // This polluter will change the fill of the node. This only works if
+ // the test node is of a shape type (the code which creates the test
+ // parameters will make sure to only use polluteFill with creators
+ // which are create Shapes)
+ final Polluter polluteFill = new Polluter() {
+ @Override public void pollute(NGNode node) {
+ if (node instanceof NGShape) {
+ com.sun.javafx.sg.prism.NGShape shape = (NGShape)node;
+ shape.setFillPaint(new Color(.43f, .23f, .66f, 1f));
+ } else if (node instanceof NGRegion) {
+ NGRegion region = (NGRegion)node;
+ javafx.scene.paint.Color color = new javafx.scene.paint.Color(.43f, .23f, .66f, 1f);
+ // I have to do this nasty reflection trickery because we don't have a Toolkit for creating
+ // the Prism Color that is the platform peer.
+ try {
+ Field platformPaint = color.getClass().getDeclaredField("platformPaint");
+ platformPaint.setAccessible(true);
+ platformPaint.set(color, new Color(.43f, .23f, .66f, 1f));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ region.updateBackground(new Background(new BackgroundFill[] {
+ new BackgroundFill(
+ color,
+ CornerRadii.EMPTY, Insets.EMPTY)
+ }));
+ } else {
+ throw new IllegalArgumentException("I don't know how to make the fill dirty on " + node);
+ }
+ }
+ @Override public String toString() { return "Pollute Fill"; }
+ };
+ // This polluter will translate the test node in a positive direction
+ final Polluter pollutePositiveTranslation = new Polluter() {
+ { tx = BaseTransform.getTranslateInstance(50, 50); }
+ @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
+ @Override public String toString() { return "Pollute Positive Translation"; }
+ };
+ // This polluter will translate the test node in a negative direction
+ final Polluter polluteNegativeTranslation = new Polluter() {
+ { tx = BaseTransform.getTranslateInstance(-50, -50); }
+ @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
+ @Override public String toString() { return "Pollute Negative Translation"; }
+ };
+ // This polluter will give the test node a scale causing it to get bigger
+ final Polluter polluteBiggerScale = new Polluter() {
+ { tx = BaseTransform.getScaleInstance(2, 2); }
+ @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
+ @Override public String toString() { return "Pollute Bigger Scale"; }
+ };
+ // This polluter will give the test node a scale causing it to get smaller
+ final Polluter polluteSmallerScale = new Polluter() {
+ { tx = BaseTransform.getScaleInstance(.5, .5); }
+ @Override public void pollute(NGNode node) { DirtyRegionTestBase.transform(node, tx); }
+ @Override public String toString() { return "Pollute Smaller Scale"; }
+ };
+ // This polluter will rotate the node about its center
+ final Polluter polluteRotate = new Polluter() {
+ @Override public void pollute(NGNode node) {
+ BaseBounds effectBounds = node.getEffectBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
+ BaseTransform tx = BaseTransform.getRotateInstance(45, effectBounds.getWidth()/2f, effectBounds.getHeight()/2f);
+ DirtyRegionTestBase.transform(node, tx);
+ }
+ @Override public BaseBounds modifiedBounds(NGNode node) {
+ BaseBounds effectBounds = node.getEffectBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
+ BaseTransform tx = BaseTransform.getRotateInstance(45, effectBounds.getWidth() / 2f, effectBounds.getHeight() / 2f);
+ return DirtyRegionTestBase.getWhatTransformedBoundsWouldBe(node, tx);
+ }
+ @Override public String toString() { return "Pollute Rotate"; }
+ };
+ // This polluter will make the test node invisible
+ final Polluter polluteVisibility = new Polluter() {
+ @Override public void pollute(NGNode node) {
+ node.setVisible(false);
+ }
+ @Override public String toString() { return "Pollute Visibility"; }
+ };
+ // This polluter will make an invisible node visible again
+ final Polluter restoreVisibility = new Polluter() {
+ @Override public void pollute(NGNode node) {
+ // I need to hide the node, and then clean up all the dirty
+ // state associated with it, and then make it visible again.
+ // This simulates making it invisible, painting, and then
+ // making it visible again.
+ node.setVisible(false);
+ NGNode parent = node;
+ while(parent.getParent() != null) parent = parent.getParent();
+ parent.render(TestGraphics.TEST_GRAPHICS);
+ // Now we can go ahead and set the opacity
+ node.setVisible(true);
+ }
+ @Override public String toString() { return "Restore Visibility"; }
+ };
+
+ // We will populate this list with the parameters with which we will test.
+ // Each Object[] within the params is composed of a Creator and a Polluter.
+ List params = new ArrayList();
+ // A standard list of polluters which applies to all tests
+ List polluters = Arrays.asList(new Polluter[]{
+ polluteRotate,
+ polluteOpacity,
+ restoreOpacity,
+ polluteVisibility,
+ restoreVisibility,
+ polluteSmallerScale,
+ polluteNegativeTranslation,
+ polluteBiggerScale,
+ pollutePositiveTranslation
+ });
+ // Construct the Creator / Polluter pair for Groups
+ for (final Polluter polluter : polluters) {
+ params.add(new Object[] {new Creator() {
+ @Override public NGNode create() { return createGroup(createRectangle(0, 0, 100, 100)); }
+ @Override public String toString() { return "Group with one Rectangle"; }
+ }, polluter});
+ }
+ // Construct the Creator / Polluter pair for Rectangles
+ List rectanglePolluters = new ArrayList(polluters);
+ rectanglePolluters.add(new Polluter() {
+ @Override public void pollute(NGNode node) {
+ NGRectangle rect = (NGRectangle)node;
+ BaseBounds bounds = rect.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
+ rect.updateRectangle(bounds.getMinX(), bounds.getMinY(), 25, 25, 0, 0);
+ }
+ @Override public String toString() { return "Pollute Rectangle Geometry"; }
+ });
+ for (final Polluter polluter : rectanglePolluters) {
+ params.add(new Object[] {new Creator() {
+ @Override public NGNode create() { return createRectangle(0, 0, 100, 100); }
+ @Override public String toString() { return "Rectangle"; }
+ }, polluter});
+ }
+ // Construct the Creator / Polluter pair for Circles
+ List circlePolluters = new ArrayList(polluters);
+ circlePolluters.add(new Polluter() {
+ @Override public void pollute(NGNode node) {
+ NGCircle c = (NGCircle)node;
+ BaseBounds bounds = c.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
+ c.updateCircle(
+ bounds.getMinX() + (bounds.getWidth()/2f),
+ bounds.getMinY() + (bounds.getHeight()/2f),
+ 10);
+ }
+ @Override public String toString() { return "Pollute Circle Geometry"; }
+ });
+ for (final Polluter polluter : circlePolluters) {
+ params.add(new Object[] {new Creator() {
+ @Override public NGNode create() { return createCircle(50, 50, 50); }
+ @Override public String toString() { return "Circle"; }
+ }, polluter});
+ }
+ // Return the populated params collection
+ return params;
+ }
+
+ /**
+ * The test node creator. This is called from within the "setUp" method in each
+ * subclass to create the nodes that are going to be tested.
+ */
+ protected Creator creator;
+
+ /**
+ * The polluter. Subclasses will use the polluter to make a node dirty at the
+ * appropriate time in the test method.
+ */
+ protected Polluter polluter;
+
+ /**
+ * The root node. This must be created during the "setUp" method in each sub
+ * class. The root is needed for actually accumulating dirty regions.
+ */
+ protected TestNGGroup root;
+
+ /**
+ * The clip to use when accumulating dirty regions. By default it is
+ * ridiculously large, such that none of the tests will ever bump up
+ * against the clip. Subclasses should however implement some tests in
+ * which they will set the clip to a specific value, and then test
+ * whether accumulating dirty regions takes the clip into account.
+ */
+ protected RectBounds windowClip = new RectBounds(-100000, -100000, 100000, 10000);
+
+ /**
+ * Creates a new DirtyRegionTestBase. Each subclass must have an identical
+ * constructor which simply passes the creator and polluter to this
+ * constructor. These instances are passed to the constructor by JUnit,
+ * so sub classes don't need to worry about creating these instances
+ * (and in fact must not do so).
+ */
+ protected DirtyRegionTestBase(Creator creator, Polluter polluter) {
+ this.creator = creator;
+ this.polluter = polluter;
+ }
+
+ /**
+ * Helper method for asserting that the dirty region of the node indicated
+ * (start) matches the dirty region which is expected. This method will
+ * invoke the accumulateDirtyRegions method on the start node. Accumulating
+ * dirty regions requires a clip to be sent along as well. The windowClip is
+ * specified on DirtyRegionTestBase (by default it is ridiculously large)
+ * but sub classes can change the clip at any time.
+ *
+ */
+ protected void assertDirtyRegionEquals(NGNode start, RectBounds expected) {
+ // TODO The root might have changes to its bounds and we should reset these. (RT-26928)
+ //DirtyRegionTestBase.resetGroupBounds(root);
+ // Accumulate the dirty region, using the windowClip.
+ // TODO if we wanted to, we could also make the device space transform parameterized
+ // such that we could test that the dirty region accumulation logic all works
+ // correctly even in the presence of a non-identity device space transform
+ // (RT-26928)
+ DirtyRegionPool pool = new DirtyRegionPool(1);
+ DirtyRegionContainer drc = pool.checkOut();
+ int status = start.accumulateDirtyRegions(
+ windowClip,
+ new RectBounds(), pool,
+ drc,
+ BaseTransform.IDENTITY_TRANSFORM, new GeneralTransform3D());
+
+ RectBounds dirtyRegion = drc.getDirtyRegion(0) ;
+
+ // The accumulation of dirty regions ends up with a slightly
+ // padded dirty region, just to make up for any error when
+ // transforming. Its quick and dirty but does the job.
+ // Perhaps we could avoid adding the slop in the case where
+ // there is no rotation or skew involved, but for now we
+ // don't. I don't want to populate all my tests with this
+ // assumption though in case it ever changes. So I am going
+ // to pad the expected here.
+ expected = new RectBounds(
+ Math.max(expected.getMinX() - 1, dirtyRegion.getMinX()),
+ Math.max(expected.getMinY() - 1, dirtyRegion.getMinY()),
+ Math.min(expected.getMaxX() + 1, dirtyRegion.getMaxX()),
+ Math.min(expected.getMaxY() + 1, dirtyRegion.getMaxY()));
+ // Now make the check, and print useful error information in case it fails.
+ assertEquals("creator=" + creator + ", polluter=" + polluter, expected, dirtyRegion);
+ }
+
+ /**
+ * Helper method for asserting that the dirty region of the node indicated
+ * (start) contains the windowClip and matches the dirty region which is
+ * expected. This method will invoke the accumulateDirtyRegions method
+ * on the start node. Accumulating dirty regions requires a clip to be sent
+ * along as well. The windowClip is specified on DirtyRegionTestBase
+ * (by default it is ridiculously large) but sub classes can change the clip
+ * at any time.
+ */
+ protected void assertContainsClip(NGNode start, RectBounds expectedDirtyRegion, int expectedStatus) {
+ DirtyRegionPool pool = new DirtyRegionPool(1);
+ DirtyRegionContainer drc = pool.checkOut();
+ int status = start.accumulateDirtyRegions(
+ windowClip,
+ new RectBounds(), pool,
+ drc,
+ BaseTransform.IDENTITY_TRANSFORM, new GeneralTransform3D());
+
+ assertEquals(expectedStatus, status);
+
+ if (status == DirtyRegionContainer.DTR_OK) {
+ assertDirtyRegionEquals(start, expectedDirtyRegion);
+ }
+ }
+
+ /**
+ * Accumulates the dirty region, and checks to make sure the the accumulateDirtyRegion
+ * method was only called on the nodes supplied. If any node in the tree (starting with
+ * and including root) have had accumulateDirtyRegion called and they are NOT in this
+ * list of expected nodes, then the assertion fails.
+ *
+ * This assertion is used to make sure that various performance optimizations are
+ * implemented correctly, such that we are not asking nodes to accumulate dirty regions
+ * who have no hope of being in the dirty region (such as children of a clean group).
+ *
+ * @param nodes A non-null array of nodes.
+ */
+ protected void assertOnlyTheseNodesAreAskedToAccumulateDirtyRegions(NGNode... nodes) {
+ accumulateDirtyRegions();
+ Set set = new HashSet(Arrays.asList(nodes));
+ assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(root, set);
+ }
+
+ /**
+ * Accumulates the dirty region, and checks to make sure that the dirty region
+ * computation methods (accumulateNodeDirtyRegion, computeDirtyRegion,
+ * accumulateGroupDirtyRegion) are only called on the nodes supplied. If any
+ * node in the tree (starting with and including the root) has had one or more
+ * of these methods called and they are NOT in the array of expected nodes, then
+ * the assertion fails.
+ *
+ * This assertion is used to make sure various performance optimizations are
+ * implemented correctly, such that even if a node is asked to accumulate its
+ * dirty region, it doesn't actually do any work if the node is actually clean.
+ *
+ * @param nodes A non-null array of nodes.
+ */
+ protected void assertOnlyTheseNodesAreAskedToComputeDirtyRegions(NGNode... nodes) {
+ accumulateDirtyRegions();
+ Set set = new HashSet(Arrays.asList(nodes));
+ assertOnlyTheseNodesWereAskedToComputeDirtyRegions(root, set);
+ }
+
+ /**
+ * Helper method which will reset the group bounds of the root prior to accumulating
+ * dirty regions. The root node might have new bounds due to changes in its children,
+ * such as transforms or geometry changes.
+ */
+ private void accumulateDirtyRegions() {
+ DirtyRegionPool pool = new DirtyRegionPool(1);
+ DirtyRegionTestBase.resetGroupBounds(root);
+ root.accumulateDirtyRegions(
+ new RectBounds(0, 0, 800, 600),
+ new RectBounds(), pool,
+ pool.checkOut(),
+ BaseTransform.IDENTITY_TRANSFORM,
+ new GeneralTransform3D());
+ }
+
+ /**
+ * Helper which walks down the tree checking to see if accumulateDirtyRegion has been
+ * called, and throws an exception if it was not expected.
+ */
+ private void assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(NGNode start, Set nodes) {
+ assertEquals(
+ "creator=" + creator + ", polluter=" + polluter,
+ nodes.contains(start), ((TestNGNode)start).askedToAccumulateDirtyRegion());
+ if (start instanceof NGGroup) {
+ for (NGNode child : ((NGGroup)start).getChildren()) {
+ assertOnlyTheseNodesWereAskedToAccumulateDirtyRegions(child, nodes);
+ }
+ }
+ }
+
+ /**
+ * Helper which walks down the tree checking to see if any of the methods which actually
+ * compute the dirty region have been called, and throws an exception if it was not expected.
+ */
+ private void assertOnlyTheseNodesWereAskedToComputeDirtyRegions(NGNode start, Set nodes) {
+ assertEquals(
+ "creator=" + creator + ", polluter=" + polluter,
+ nodes.contains(start), ((TestNGNode)start).computedDirtyRegion());
+ if (start instanceof NGGroup) {
+ for (NGNode child : ((NGGroup)start).getChildren()) {
+ assertOnlyTheseNodesWereAskedToComputeDirtyRegions(child, nodes);
+ }
+ }
+ }
+
+ static protected void resetGroupBounds(NGGroup group) {
+ BaseBounds contentBounds = new RectBounds();
+ for (NGNode child : group.getChildren()) {
+ contentBounds = contentBounds.deriveWithUnion(
+ child.getCompleteBounds(
+ new RectBounds(), BaseTransform.IDENTITY_TRANSFORM));
+ }
+ BaseBounds currentContentBounds = group.getContentBounds(new RectBounds(), BaseTransform.IDENTITY_TRANSFORM);
+ if (!contentBounds.equals(currentContentBounds)) {
+ System.out.println("CurrentContentBounds=" + currentContentBounds + ", bounds=" + contentBounds);
+ group.setContentBounds(contentBounds);
+ group.setTransformedBounds(group.getEffectBounds(new RectBounds(), group.getTransform()), false);
+ }
+ }
+
+ /**
+ * Sort of a non-applying version of the transform method. This method will
+ * compute and return what the transformed bounds of the given node would
+ * be if the transform were applied to it.
+ */
+ static protected BaseBounds getWhatTransformedBoundsWouldBe(NGNode node, BaseTransform tx) {
+ BaseTransform existing = BaseTransform.IDENTITY_TRANSFORM.deriveWithNewTransform(node.getTransform());
+ tx = existing.deriveWithConcatenation(tx);
+ return node.getEffectBounds(new RectBounds(), tx);
+ }
+}