/*
* 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