1 /* 2 * Copyright (c) 2012, 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 javafx.scene.canvas; 27 28 import javafx.beans.property.DoubleProperty; 29 import javafx.beans.property.DoublePropertyBase; 30 import javafx.geometry.NodeOrientation; 31 import javafx.scene.Node; 32 import com.sun.javafx.geom.BaseBounds; 33 import com.sun.javafx.geom.RectBounds; 34 import com.sun.javafx.geom.transform.BaseTransform; 35 import com.sun.javafx.jmx.MXNodeAlgorithm; 36 import com.sun.javafx.jmx.MXNodeAlgorithmContext; 37 import com.sun.javafx.scene.DirtyBits; 38 import com.sun.javafx.scene.NodeHelper; 39 import com.sun.javafx.scene.canvas.CanvasHelper; 40 import com.sun.javafx.sg.prism.GrowableDataBuffer; 41 import com.sun.javafx.sg.prism.NGCanvas; 42 import com.sun.javafx.sg.prism.NGNode; 43 44 /** 45 * {@code Canvas} is an image that can be drawn on using a set of graphics 46 * commands provided by a {@code GraphicsContext}. 47 * 48 * <p> 49 * A {@code Canvas} node is constructed with a width and height that specifies the size 50 * of the image into which the canvas drawing commands are rendered. All drawing 51 * operations are clipped to the bounds of that image. 52 * 53 * <p>Example:</p> 54 * 55 * <p> 56 * <pre> 57 import javafx.scene.*; 58 import javafx.scene.paint.*; 59 import javafx.scene.canvas.*; 60 61 Group root = new Group(); 62 Scene s = new Scene(root, 300, 300, Color.BLACK); 63 64 final Canvas canvas = new Canvas(250,250); 65 GraphicsContext gc = canvas.getGraphicsContext2D(); 66 67 gc.setFill(Color.BLUE); 68 gc.fillRect(75,75,100,100); 69 70 root.getChildren().add(canvas); 71 * </pre> 72 * </p> 73 * 74 * @since JavaFX 2.2 75 */ 76 public class Canvas extends Node { 77 static { 78 CanvasHelper.setCanvasAccessor(new CanvasHelper.CanvasAccessor() { 79 @Override 80 public NGNode doCreatePeer(Node node) { 81 return ((Canvas) node).doCreatePeer(); 82 } 83 84 @Override 85 public void doUpdatePeer(Node node) { 86 ((Canvas) node).doUpdatePeer(); 87 } 88 }); 89 } 90 static final int DEFAULT_VAL_BUF_SIZE = 1024; 91 static final int DEFAULT_OBJ_BUF_SIZE = 32; 92 private static final int SIZE_HISTORY = 5; 93 94 private GrowableDataBuffer current; 95 private boolean rendererBehind; 96 private int recentvalsizes[]; 97 private int recentobjsizes[]; 98 private int lastsizeindex; 99 100 private GraphicsContext theContext; 101 102 { 103 // To initialize the class helper at the begining each constructor of this class 104 CanvasHelper.initHelper(this); 105 } 106 107 /** 108 * Creates an empty instance of Canvas. 109 */ 110 public Canvas() { 111 this(0, 0); 112 } 113 114 /** 115 * Creates a new instance of Canvas with the given size. 116 * 117 * @param width width of the canvas 118 * @param height height of the canvas 119 */ 120 public Canvas(double width, double height) { 121 this.recentvalsizes = new int[SIZE_HISTORY]; 122 this.recentobjsizes = new int[SIZE_HISTORY]; 123 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); 124 setWidth(width); 125 setHeight(height); 126 } 127 128 private static int max(int sizes[], int defsize) { 129 for (int s : sizes) { 130 if (defsize < s) defsize = s; 131 } 132 return defsize; 133 } 134 135 GrowableDataBuffer getBuffer() { 136 NodeHelper.markDirty(this, DirtyBits.NODE_CONTENTS); 137 NodeHelper.markDirty(this, DirtyBits.NODE_FORCE_SYNC); 138 if (current == null) { 139 int vsize = max(recentvalsizes, DEFAULT_VAL_BUF_SIZE); 140 int osize = max(recentobjsizes, DEFAULT_OBJ_BUF_SIZE); 141 current = GrowableDataBuffer.getBuffer(vsize, osize); 142 theContext.updateDimensions(); 143 } 144 return current; 145 } 146 147 boolean isRendererFallingBehind() { 148 return rendererBehind; 149 } 150 151 /** 152 * returns the {@code GraphicsContext} associated with this {@code Canvas}. 153 */ 154 public GraphicsContext getGraphicsContext2D() { 155 if (theContext == null) { 156 theContext = new GraphicsContext(this); 157 } 158 return theContext; 159 } 160 161 /** 162 * Defines the width of the canvas. 163 * 164 * @profile common 165 * @defaultvalue 0.0 166 */ 167 private DoubleProperty width; 168 169 public final void setWidth(double value) { 170 widthProperty().set(value); 171 } 172 173 public final double getWidth() { 174 return width == null ? 0.0 : width.get(); 175 } 176 177 public final DoubleProperty widthProperty() { 178 if (width == null) { 179 width = new DoublePropertyBase() { 180 181 @Override 182 public void invalidated() { 183 NodeHelper.markDirty(Canvas.this, DirtyBits.NODE_GEOMETRY); 184 impl_geomChanged(); 185 if (theContext != null) { 186 theContext.updateDimensions(); 187 } 188 } 189 190 @Override 191 public Object getBean() { 192 return Canvas.this; 193 } 194 195 @Override 196 public String getName() { 197 return "width"; 198 } 199 }; 200 } 201 return width; 202 } 203 204 /** 205 * Defines the height of the canvas. 206 * 207 * @profile common 208 * @defaultvalue 0.0 209 */ 210 private DoubleProperty height; 211 212 public final void setHeight(double value) { 213 heightProperty().set(value); 214 } 215 216 public final double getHeight() { 217 return height == null ? 0.0 : height.get(); 218 } 219 220 public final DoubleProperty heightProperty() { 221 if (height == null) { 222 height = new DoublePropertyBase() { 223 224 @Override 225 public void invalidated() { 226 NodeHelper.markDirty(Canvas.this, DirtyBits.NODE_GEOMETRY); 227 impl_geomChanged(); 228 if (theContext != null) { 229 theContext.updateDimensions(); 230 } 231 } 232 233 @Override 234 public Object getBean() { 235 return Canvas.this; 236 } 237 238 @Override 239 public String getName() { 240 return "height"; 241 } 242 }; 243 } 244 return height; 245 } 246 247 private NGNode doCreatePeer() { 248 return new NGCanvas(); 249 } 250 251 /* 252 * Note: This method MUST only be called via its accessor method. 253 */ 254 private void doUpdatePeer() { 255 if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) { 256 NGCanvas peer = NodeHelper.getPeer(this); 257 peer.updateBounds((float)getWidth(), 258 (float)getHeight()); 259 } 260 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) { 261 NGCanvas peer = NodeHelper.getPeer(this); 262 if (current != null && !current.isEmpty()) { 263 if (--lastsizeindex < 0) { 264 lastsizeindex = SIZE_HISTORY - 1; 265 } 266 recentvalsizes[lastsizeindex] = current.writeValuePosition(); 267 recentobjsizes[lastsizeindex] = current.writeObjectPosition(); 268 rendererBehind = peer.updateRendering(current); 269 current = null; 270 } 271 } 272 } 273 274 /** 275 * @treatAsPrivate implementation detail 276 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 277 */ 278 @Deprecated 279 @Override 280 protected boolean impl_computeContains(double localX, double localY) { 281 double w = getWidth(); 282 double h = getHeight(); 283 return (w > 0 && h > 0 && 284 localX >= 0 && localY >= 0 && 285 localX < w && localY < h); 286 } 287 288 /** 289 * @treatAsPrivate implementation detail 290 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 291 */ 292 @Deprecated 293 @Override 294 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 295 bounds = new RectBounds(0f, 0f, (float) getWidth(), (float) getHeight()); 296 bounds = tx.transform(bounds, bounds); 297 return bounds; 298 } 299 300 /** 301 * @treatAsPrivate implementation detail 302 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 303 */ 304 @Deprecated 305 @Override 306 public Object impl_processMXNode(MXNodeAlgorithm alg, 307 MXNodeAlgorithmContext ctx) { 308 return alg.processLeafNode(this, ctx); 309 } 310 }