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 }