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 com.sun.javafx.geom.Arc2D;
  29 import com.sun.javafx.geom.IllegalPathStateException;
  30 import com.sun.javafx.geom.Path2D;
  31 import com.sun.javafx.geom.PathIterator;
  32 import com.sun.javafx.geom.transform.Affine2D;
  33 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
  34 import com.sun.javafx.image.*;
  35 import com.sun.javafx.image.impl.ByteBgraPre;
  36 import com.sun.javafx.sg.prism.GrowableDataBuffer;
  37 import com.sun.javafx.sg.prism.NGCanvas;
  38 import com.sun.javafx.tk.Toolkit;
  39 import javafx.geometry.NodeOrientation;
  40 import javafx.geometry.VPos;
  41 import javafx.scene.effect.Blend;
  42 import javafx.scene.effect.BlendMode;
  43 import javafx.scene.effect.Effect;
  44 import javafx.scene.image.Image;
  45 import javafx.scene.image.PixelFormat;
  46 import javafx.scene.image.PixelReader;
  47 import javafx.scene.image.PixelWriter;
  48 import javafx.scene.paint.Color;
  49 import javafx.scene.paint.Paint;
  50 import javafx.scene.shape.ArcType;
  51 import javafx.scene.shape.FillRule;
  52 import javafx.scene.shape.StrokeLineCap;
  53 import javafx.scene.shape.StrokeLineJoin;
  54 import javafx.scene.text.Font;
  55 import javafx.scene.text.TextAlignment;
  56 import javafx.scene.transform.Affine;
  57 
  58 import java.nio.Buffer;
  59 import java.nio.ByteBuffer;
  60 import java.nio.IntBuffer;
  61 import java.util.LinkedList;
  62 
  63 /**
  64  * This class is used to issue draw calls to a {@code Canvas} using a buffer. 
  65  * <p>
  66  * Each call pushes the necessary parameters onto the buffer
  67  * where they will be later rendered onto the image of the {@code Canvas} node
  68  * by the rendering thread at the end of a pulse.
  69  * <p>
  70  * A {@code Canvas} only contains one {@code GraphicsContext}, and only one buffer.
  71  * If it is not attached to any scene, then it can be modified by any thread,
  72  * as long as it is only used from one thread at a time. Once a {@code Canvas} 
  73  * node is attached to a scene, it must be modified on the JavaFX Application 
  74  * Thread.
  75  * <p>
  76  * Calling any method on the {@code GraphicsContext} is considered modifying
  77  * its corresponding {@code Canvas} and is subject to the same threading
  78  * rules. 
  79  * <p>
  80  * A {@code GraphicsContext} also manages a stack of state objects that can
  81  * be saved or restored at anytime.
  82  * <p>
  83  * The {@code GraphicsContext} maintains the following rendering attributes
  84  * which affect various subsets of the rendering methods:
  85  * <table class="overviewSummary" style="width:80%; margin-left:auto; margin-right:auto">
  86  * <tr>
  87  * <th class="colLast" style="width:15%">Attribute</th>
  88  * <th class="colLast" style="width:10%; text-align:center">Save/Restore?</th>
  89  * <th class="colLast" style="width:10%; text-align:center">Default value</th>
  90  * <th class="colLast">Description</th>
  91  * </tr>
  92  * 
  93  * <tr><th colspan="3"><a name="comm-attr"><p align="center">Common Rendering Attributes</p></a></th></tr>
  94  * <tr class="rowColor">
  95  * <td class="colLast" style="width:15%">{@link #clip() Clip}</td>
  96  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
  97  * <td class="colLast" style="width:10%; text-align:center">No clipping</td>
  98  * <td class="colLast">
  99  * An anti-aliased intersection of various clip paths to which rendering
 100  * is restricted.
 101  * </td></tr>
 102  * <tr class="altColor">
 103  * <td class="colLast" style="width:15%">{@link #setGlobalAlpha(double) Global Alpha}</td>
 104  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 105  * <td class="colLast" style="width:10%; text-align:center">{@code 1.0}</td>
 106  * <td class="colLast">
 107  * An opacity value that controls the visibility or fading of each rendering
 108  * operation.
 109  * </td></tr>
 110  * <tr class="rowColor">
 111  * <td class="colLast" style="width:15%">{@link #setGlobalBlendMode(javafx.scene.effect.BlendMode) Global Blend Mode}</td>
 112  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 113  * <td class="colLast" style="width:10%; text-align:center">{@link BlendMode#SRC_OVER SRC_OVER}</td>
 114  * <td class="colLast">
 115  * A {@link BlendMode} enum value that controls how pixels from each rendering
 116  * operation are composited into the existing image.
 117  * </td></tr>
 118  * <tr class="altColor">
 119  * <td class="colLast" style="width:15%">{@link #setTransform(javafx.scene.transform.Affine) Transform}</td>
 120  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 121  * <td class="colLast" style="width:10%; text-align:center">{@code Identity}</td>
 122  * <td class="colLast">
 123  * A 3x2 2D affine transformation matrix that controls how coordinates are
 124  * mapped onto the logical pixels of the canvas image.
 125  * </td></tr>
 126  * <tr class="altColor">
 127  * <td class="colLast" style="width:15%">{@link #setEffect(javafx.scene.effect.Effect) Effect}</td>
 128  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 129  * <td class="colLast" style="width:10%; text-align:center">{@code null}</td>
 130  * <td class="colLast">
 131  * An {@link Effect} applied individually to each rendering operation.
 132  * </td></tr>
 133  * 
 134  * <tr><th colspan="3"><a name="fill-attr"><p align="center">Fill Attributes</p></a></th></tr>
 135  * <tr class="rowColor">
 136  * <td class="colLast" style="width:15%">{@link #setFill(javafx.scene.paint.Paint) Fill Paint}</td>
 137  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 138  * <td class="colLast" style="width:10%; text-align:center">{@link Color#BLACK BLACK}</td>
 139  * <td class="colLast">
 140  * The {@link Paint} to be applied to the interior of shapes in a
 141  * fill operation.
 142  * </td></tr>
 143  * 
 144  * <tr><th colspan="3"><a name="strk-attr"><p align="center">Stroke Attributes</p></a></th></tr>
 145  * <tr class="rowColor">
 146  * <td class="colLast" style="width:15%">{@link #setStroke(javafx.scene.paint.Paint) Stroke Paint}</td>
 147  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 148  * <td class="colLast" style="width:10%; text-align:center">{@link Color#BLACK BLACK}</td>
 149  * <td class="colLast">
 150  * The {@link Paint} to be applied to the boundary of shapes in a
 151  * stroke operation.
 152  * </td></tr>
 153  * <tr class="altColor">
 154  * <td class="colLast" style="width:15%">{@link #setLineWidth(double) Line Width}</td>
 155  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 156  * <td class="colLast" style="width:10%; text-align:center">{@code 1.0}</td>
 157  * <td class="colLast">
 158  * The width of the stroke applied to the boundary of shapes in a
 159  * stroke operation.
 160  * </td></tr>
 161  * <tr class="rowColor">
 162  * <td class="colLast" style="width:15%">{@link #setLineCap(javafx.scene.shape.StrokeLineCap) Line Cap}</td>
 163  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 164  * <td class="colLast" style="width:10%; text-align:center">{@link StrokeLineCap#SQUARE SQUARE}</td>
 165  * <td class="colLast">
 166  * The style of the end caps applied to the beginnings and ends of each
 167  * dash and/or subpath in a stroke operation.
 168  * </td></tr>
 169  * <tr class="altColor">
 170  * <td class="colLast" style="width:15%">{@link #setLineJoin(javafx.scene.shape.StrokeLineJoin) Line Join}</td>
 171  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 172  * <td class="colLast" style="width:10%; text-align:center">{@link StrokeLineJoin#MITER MITER}</td>
 173  * <td class="colLast">
 174  * The style of the joins applied between individual segments in the boundary
 175  * paths of shapes in a stroke operation.
 176  * </td></tr>
 177  * <tr class="rowColor">
 178  * <td class="colLast" style="width:15%">{@link #setMiterLimit(double) Miter Limit}</td>
 179  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 180  * <td class="colLast" style="width:10%; text-align:center">{@code 10.0}</td>
 181  * <td class="colLast">
 182  * The ratio limit of how far a {@link StrokeLineJoin#MITER MITER} line join
 183  * may extend in the direction of a sharp corner between segments in the
 184  * boundary path of a shape, relative to the line width, before it is truncated
 185  * to a {@link StrokeLineJoin#BEVEL BEVEL} join in a stroke operation.
 186  * </td></tr>
 187  * 
 188  * <tr><th colspan="3"><a name="text-attr"><p align="center">Text Attributes</p></a></th></tr>
 189  * <tr class="rowColor">
 190  * <td class="colLast" style="width:15%">{@link #setFont(javafx.scene.text.Font) Font}</td>
 191  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 192  * <td class="colLast" style="width:10%; text-align:center">{@link Font#getDefault() Default Font}</td>
 193  * <td class="colLast">
 194  * The font used for all fill and stroke text operations.
 195  * </td></tr>
 196  * <tr class="altColor">
 197  * <td class="colLast" style="width:15%">{@link #setTextAlign(javafx.scene.text.TextAlignment) Text Align}</td>
 198  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 199  * <td class="colLast" style="width:10%; text-align:center">{@link TextAlignment#LEFT LEFT}</td>
 200  * <td class="colLast">
 201  * The horizontal alignment of text with respect to the {@code X} coordinate
 202  * specified in the text operation.
 203  * </td></tr>
 204  * <tr class="rowColor">
 205  * <td class="colLast" style="width:15%">{@link #setTextBaseline(javafx.geometry.VPos) Text Baseline}</td>
 206  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 207  * <td class="colLast" style="width:10%; text-align:center">{@link VPos#BASELINE BASELINE}</td>
 208  * <td class="colLast">
 209  * The vertical position of the text relative to the {@code Y} coordinate
 210  * specified in the text operation.
 211  * </td></tr>
 212  * 
 213  * <tr><th colspan="3"><a name="path-attr"><p align="center">Path Attributes</p></a></th></tr>
 214  * <tr class="rowColor">
 215  * <td class="colLast" style="width:15%">{@link #beginPath() Current Path}</td>
 216  * <td class="colLast" style="width:10%; text-align:center; color:#c00">No</td>
 217  * <td class="colLast" style="width:10%; text-align:center">Empty path</td>
 218  * <td class="colLast">
 219  * The path constructed using various path construction methods to be used
 220  * in various path filling, stroking, or clipping operations.
 221  * </td></tr>
 222  * <tr class="altColor">
 223  * <td class="colLast" style="width:15%">{@link #setFillRule(javafx.scene.shape.FillRule) Fill Rule}</td>
 224  * <td class="colLast" style="width:10%; text-align:center; color:#0c0">Yes</td>
 225  * <td class="colLast" style="width:10%; text-align:center">{@link FillRule#NON_ZERO NON_ZERO}</td>
 226  * <td class="colLast">
 227  * The method used to determine the interior of paths for a path fill or
 228  * clip operation.
 229  * </td></tr>
 230  * </table>
 231  * 
 232  * <p>
 233  * <a name="attr-ops-table">
 234  * The various rendering methods on the {@code GraphicsContext} use the
 235  * following sets of rendering attributes:
 236  * </a>
 237  * <table class="overviewSummary" style="width:80%; margin-left:auto; margin-right:auto">
 238  * <tr>
 239  * <th class="colLast" style="width:25%">Method</th>
 240  * <th class="colLast" style="width:15%; text-align:center"><a href="#comm-attr">Common Rendering Attributes</a></th>
 241  * <th class="colLast" style="width:15%; text-align:center"><a href="#fill-attr">Fill Attributes</a></th>
 242  * <th class="colLast" style="width:15%; text-align:center"><a href="#strk-attr">Stroke Attributes</a></th>
 243  * <th class="colLast" style="width:15%; text-align:center"><a href="#text-attr">Text Attributes</a></th>
 244  * <th class="colLast" style="width:15%; text-align:center"><a href="#path-attr">Path Attributes</a></th>
 245  * </tr>
 246  * 
 247  * <tr><th colspan="1"><p align="center">Basic Shape Rendering</p></th></tr>
 248  * <tr class="rowColor">
 249  * <td class="colLast" style="width:25%">
 250  * <a name="fill-basic-ops">
 251  * {@link #fillRect(double, double, double, double) fillRect()},
 252  * {@link #fillRoundRect(double, double, double, double, double, double) fillRoundRect()},
 253  * {@link #fillOval(double, double, double, double) fillOval()},
 254  * {@link #fillArc(double, double, double, double, double, double, javafx.scene.shape.ArcType) fillArc()}
 255  * </a>
 256  * </td>
 257  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 258  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 259  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 260  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 261  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 262  * </tr>
 263  * <tr class="altColor">
 264  * <td class="colLast" style="width:25%">
 265  * <a name="strk-basic-ops">
 266  * {@link #strokeLine(double, double, double, double) strokeLine()},
 267  * {@link #strokeRect(double, double, double, double) strokeRect()},
 268  * {@link #strokeRoundRect(double, double, double, double, double, double) strokeRoundRect()},
 269  * {@link #strokeOval(double, double, double, double) strokeOval()},
 270  * {@link #strokeArc(double, double, double, double, double, double, javafx.scene.shape.ArcType) strokeArc()}
 271  * </a>
 272  * </td>
 273  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 274  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 275  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 276  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 277  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 278  * </tr>
 279  * <tr class="rowColor">
 280  * <td class="colLast" style="width:25%">
 281  * <a name="fill-basic-ops">
 282  * {@link #clearRect(double, double, double, double) clearRect()}
 283  * </a>
 284  * </td>
 285  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#base-fn-1">[1]</a></td>
 286  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 287  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 288  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 289  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 290  * </tr>
 291  * <tr class="altColor">
 292  * <td class="colLast" style="width:25%">
 293  * <a name="strk-basic-ops">
 294  * {@link #fillPolygon(double[], double[], int) fillPolygon()}
 295  * </a>
 296  * </td>
 297  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 298  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 299  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 300  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 301  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#base-fn-2">[2]</a></td>
 302  * </tr>
 303  * <tr class="rowColor">
 304  * <td class="colLast" style="width:25%">
 305  * <a name="strk-basic-ops">
 306  * {@link #strokePolygon(double[], double[], int) strokePolygon()},
 307  * {@link #strokePolyline(double[], double[], int) strokePolyline()}
 308  * </a>
 309  * </td>
 310  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 311  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 312  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 313  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 314  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 315  * </tr>
 316  * <tr><td colspan="6"><p align="center">
 317  * <a name="base-fn-1">[1]</a>Only the Transform, Clip, and Effect apply to clearRect()<br>
 318  * <a name="base-fn-2">[2]</a>Only the Fill Rule applies to fillPolygon(), the current path is left unchanged
 319  * </p></td></tr>
 320  * 
 321  * <tr><th colspan="1"><p align="center">Text Rendering</p></th></tr>
 322  * <tr class="rowColor">
 323  * <td class="colLast" style="width:25%">
 324  * <a name="fill-text-ops">
 325  * {@link #fillText(java.lang.String, double, double) fillText()},
 326  * {@link #fillText(java.lang.String, double, double, double) fillText(with maxWidth)}
 327  * </a>
 328  * </td>
 329  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 330  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 331  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 332  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 333  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 334  * </tr>
 335  * <tr class="altColor">
 336  * <td class="colLast" style="width:25%">
 337  * <a name="strk-text-ops">
 338  * {@link #strokeText(java.lang.String, double, double) strokeText()},
 339  * {@link #strokeText(java.lang.String, double, double, double) strokeText(with maxWidth)}
 340  * </a>
 341  * </td>
 342  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 343  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 344  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 345  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 346  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 347  * </tr>
 348  * 
 349  * <tr><th colspan="1"><p align="center">Path Rendering</p></th></tr>
 350  * <tr class="rowColor">
 351  * <td class="colLast" style="width:25%">
 352  * {@link #beginPath() beginPath()},
 353  * {@link #moveTo(double, double) moveTo()},
 354  * {@link #lineTo(double, double) lineTo()},
 355  * {@link #quadraticCurveTo(double, double, double, double) quadraticCurveTo()},
 356  * {@link #bezierCurveTo(double, double, double, double, double, double) bezierCurveTo()},
 357  * {@link #arc(double, double, double, double, double, double) arc()},
 358  * {@link #arcTo(double, double, double, double, double) arcTo()},
 359  * {@link #appendSVGPath(java.lang.String) appendSVGPath()},
 360  * {@link #closePath() closePath()},
 361  * {@link #rect(double, double, double, double) rect()}
 362  * </td>
 363  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#path-fn-3">[3]</a></td>
 364  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 365  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 366  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 367  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 368  * </tr>
 369  * <tr class="altColor">
 370  * <td class="colLast" style="width:25%">
 371  * <a name="fill-path-ops">
 372  * {@link #fill() fill()}
 373  * </a>
 374  * </td>
 375  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#path-fn-3">[3]</a></td>
 376  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 377  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 378  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 379  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 380  * </tr>
 381  * <tr class="rowColor">
 382  * <td class="colLast" style="width:25%">
 383  * <a name="strk-path-ops">
 384  * {@link #stroke() stroke()}
 385  * </a>
 386  * </td>
 387  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#path-fn-3">[3]</a></td>
 388  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 389  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 390  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 391  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes <a href="#path-fn-4">[4]</a></td>
 392  * </tr>
 393  * <tr class="altColor">
 394  * <td class="colLast" style="width:25%">
 395  * <a name="clip-path-ops">
 396  * {@link #clip() clip()}
 397  * </a>
 398  * </td>
 399  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 400  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 401  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 402  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 403  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 404  * </tr>
 405  * <tr><td colspan="6"><p align="center">
 406  * <a name="path-fn-3">[3]</a>Transform applied only during path construction<br>
 407  * <a name="path-fn-4">[4]</a>Fill Rule only used for fill() and clip()
 408  * </p></td></tr>
 409  * 
 410  * <tr><th colspan="1"><p align="center">Image Rendering</p></th></tr>
 411  * <tr class="rowColor">
 412  * <td class="colLast" style="width:25%">
 413  * <a name="draw-img-ops">
 414  * {@link #drawImage(javafx.scene.image.Image, double, double) drawImage(all forms)}
 415  * </a>
 416  * </td>
 417  * <td class="colLast" style="width:15%; text-align:center; color:#0c0">Yes</td>
 418  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 419  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 420  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 421  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 422  * </tr>
 423  * 
 424  * <tr><th colspan="1"><p align="center">Miscellaneous</p></th></tr>
 425  * <tr class="rowColor">
 426  * <td class="colLast" style="width:25%">
 427  * {@link #applyEffect(javafx.scene.effect.Effect) applyEffect()},
 428  * {@link #getPixelWriter() PixelWriter methods}
 429  * </td>
 430  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 431  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 432  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 433  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 434  * <td class="colLast" style="width:15%; text-align:center; color:#c00">No</td>
 435  * </tr>
 436  * </table>
 437  *
 438  * <p>Example:</p>
 439  *
 440  * <p>
 441  * <pre>
 442 import javafx.scene.*;
 443 import javafx.scene.paint.*;
 444 import javafx.scene.canvas.*;
 445 
 446 Group root = new Group();
 447 Scene s = new Scene(root, 300, 300, Color.BLACK);
 448 
 449 final Canvas canvas = new Canvas(250,250);
 450 GraphicsContext gc = canvas.getGraphicsContext2D();
 451  
 452 gc.setFill(Color.BLUE);
 453 gc.fillRect(75,75,100,100);
 454  
 455 root.getChildren().add(canvas);
 456  * </pre>
 457  * </p>
 458  *
 459  * @since JavaFX 2.2
 460  */
 461 public final class GraphicsContext {
 462     Canvas theCanvas;
 463     Path2D path;
 464     boolean pathDirty;
 465 
 466     State curState;
 467     LinkedList<State> stateStack;
 468     LinkedList<Path2D> clipStack;
 469 
 470     GraphicsContext(Canvas theCanvas) {
 471         this.theCanvas = theCanvas;
 472         this.path = new Path2D();
 473         pathDirty = true;
 474 
 475         this.curState = new State();
 476         this.stateStack = new LinkedList<State>();
 477         this.clipStack = new LinkedList<Path2D>();
 478     }
 479 
 480     static class State {
 481         double globalAlpha;
 482         BlendMode blendop;
 483         Affine2D transform;
 484         Paint fill;
 485         Paint stroke;
 486         double linewidth;
 487         StrokeLineCap linecap;
 488         StrokeLineJoin linejoin;
 489         double miterlimit;
 490         int numClipPaths;
 491         Font font;
 492         TextAlignment textalign;
 493         VPos textbaseline;
 494         Effect effect;
 495         FillRule fillRule;
 496 
 497         State() {
 498             init();
 499         }
 500 
 501         final void init() {
 502             set(1.0, BlendMode.SRC_OVER,
 503                 new Affine2D(),
 504                 Color.BLACK, Color.BLACK,
 505                 1.0, StrokeLineCap.SQUARE, StrokeLineJoin.MITER, 10.0,
 506                 0, Font.getDefault(), TextAlignment.LEFT, VPos.BASELINE,
 507                 null, FillRule.NON_ZERO);
 508         }
 509 
 510         State(State copy) {
 511             set(copy.globalAlpha, copy.blendop,
 512                 new Affine2D(copy.transform),
 513                 copy.fill, copy.stroke,
 514                 copy.linewidth, copy.linecap, copy.linejoin, copy.miterlimit,
 515                 copy.numClipPaths,
 516                 copy.font, copy.textalign, copy.textbaseline,
 517                 copy.effect, copy.fillRule);
 518         }
 519 
 520         State(double globalAlpha, BlendMode blendop,
 521                      Affine2D transform, Paint fill, Paint stroke,
 522                      double linewidth, StrokeLineCap linecap,
 523                      StrokeLineJoin linejoin, double miterlimit,
 524                      int numClipPaths,
 525                      Font font, TextAlignment align, VPos baseline,
 526                      Effect effect, FillRule fillRule)
 527         {
 528             set(globalAlpha, blendop,
 529                 new Affine2D(transform),
 530                 fill, stroke,
 531                 linewidth, linecap, linejoin, miterlimit,
 532                 numClipPaths,
 533                 font, textalign, textbaseline,
 534                 effect, fillRule);
 535         }
 536 
 537         final void set(double globalAlpha, BlendMode blendop,
 538                        Affine2D transform, Paint fill, Paint stroke,
 539                        double linewidth, StrokeLineCap linecap,
 540                        StrokeLineJoin linejoin, double miterlimit,
 541                        int numClipPaths,
 542                        Font font, TextAlignment align, VPos baseline,
 543                        Effect effect, FillRule fillRule)
 544         {
 545             this.globalAlpha = globalAlpha;
 546             this.blendop = blendop;
 547             this.transform = transform;
 548             this.fill = fill;
 549             this.stroke = stroke;
 550             this.linewidth = linewidth;
 551             this.linecap = linecap;
 552             this.linejoin = linejoin;
 553             this.miterlimit = miterlimit;
 554             this.numClipPaths = numClipPaths;
 555             this.font = font;
 556             this.textalign = align;
 557             this.textbaseline = baseline;
 558             this.effect = effect;
 559             this.fillRule = fillRule;
 560         }
 561 
 562         State copy() {
 563             return new State(this);
 564         }
 565 
 566         void restore(GraphicsContext ctx) {
 567             ctx.setGlobalAlpha(globalAlpha);
 568             ctx.setGlobalBlendMode(blendop);
 569             ctx.setTransform(transform.getMxx(), transform.getMyx(),
 570                              transform.getMxy(), transform.getMyy(),
 571                              transform.getMxt(), transform.getMyt());
 572             ctx.setFill(fill);
 573             ctx.setStroke(stroke);
 574             ctx.setLineWidth(linewidth);
 575             ctx.setLineCap(linecap);
 576             ctx.setLineJoin(linejoin);
 577             ctx.setMiterLimit(miterlimit);
 578             GrowableDataBuffer buf = ctx.getBuffer();
 579             while (ctx.curState.numClipPaths > numClipPaths) {
 580                 ctx.curState.numClipPaths--;
 581                 ctx.clipStack.removeLast();
 582                 buf.putByte(NGCanvas.POP_CLIP);
 583             }
 584             ctx.setFillRule(fillRule);
 585             ctx.setFont(font);
 586             ctx.setTextAlign(textalign);
 587             ctx.setTextBaseline(textbaseline);
 588             ctx.setEffect(effect);
 589         }
 590     }
 591 
 592     private GrowableDataBuffer getBuffer() {
 593         return theCanvas.getBuffer();
 594     }
 595 
 596     private float coords[] = new float[6];
 597     private static final byte pgtype[] = {
 598         NGCanvas.MOVETO,
 599         NGCanvas.LINETO,
 600         NGCanvas.QUADTO,
 601         NGCanvas.CUBICTO,
 602         NGCanvas.CLOSEPATH,
 603     };
 604     private static final int numsegs[] = { 2, 2, 4, 6, 0, };
 605 
 606     private void markPathDirty() {
 607         pathDirty = true;
 608     }
 609 
 610     private void writePath(byte command) {
 611         updateTransform();
 612         GrowableDataBuffer buf = getBuffer();
 613         if (pathDirty) {
 614             buf.putByte(NGCanvas.PATHSTART);
 615             PathIterator pi = path.getPathIterator(null);
 616             while (!pi.isDone()) {
 617                 int pitype = pi.currentSegment(coords);
 618                 buf.putByte(pgtype[pitype]);
 619                 for (int i = 0; i < numsegs[pitype]; i++) {
 620                     buf.putFloat(coords[i]);
 621                 }
 622                 pi.next();
 623             }
 624             buf.putByte(NGCanvas.PATHEND);
 625             pathDirty = false;
 626         }
 627         buf.putByte(command);
 628     }
 629 
 630     private void writePaint(Paint p, byte command) {
 631         GrowableDataBuffer buf = getBuffer();
 632         buf.putByte(command);
 633         buf.putObject(Toolkit.getPaintAccessor().getPlatformPaint(p));
 634     }
 635 
 636     private void writeArcType(ArcType closure) {
 637         byte type;
 638         switch (closure) {
 639             case OPEN:  type = NGCanvas.ARC_OPEN;  break;
 640             case CHORD: type = NGCanvas.ARC_CHORD; break;
 641             case ROUND: type = NGCanvas.ARC_PIE;   break;
 642             default: return;  // ignored for consistency with other attributes
 643         }
 644         writeParam(type, NGCanvas.ARC_TYPE);
 645     }
 646 
 647     private void writeRectParams(GrowableDataBuffer buf,
 648                                  double x, double y, double w, double h,
 649                                  byte command)
 650     {
 651         buf.putByte(command);
 652         buf.putFloat((float) x);
 653         buf.putFloat((float) y);
 654         buf.putFloat((float) w);
 655         buf.putFloat((float) h);
 656     }
 657 
 658     private void writeOp4(double x, double y, double w, double h, byte command) {
 659         updateTransform();
 660         writeRectParams(getBuffer(), x, y, w, h, command);
 661     }
 662 
 663     private void writeOp6(double x, double y, double w, double h,
 664                           double v1, double v2, byte command)
 665     {
 666         updateTransform();
 667         GrowableDataBuffer buf = getBuffer();
 668         buf.putByte(command);
 669         buf.putFloat((float) x);
 670         buf.putFloat((float) y);
 671         buf.putFloat((float) w);
 672         buf.putFloat((float) h);
 673         buf.putFloat((float) v1);
 674         buf.putFloat((float) v2);
 675     }
 676 
 677     private float polybuf[] = new float[512];
 678     private void flushPolyBuf(GrowableDataBuffer buf,
 679                               float polybuf[], int n, byte command)
 680     {
 681         curState.transform.transform(polybuf, 0, polybuf, 0, n/2);
 682         for (int i = 0; i < n; i += 2) {
 683             buf.putByte(command);
 684             buf.putFloat(polybuf[i]);
 685             buf.putFloat(polybuf[i+1]);
 686             command = NGCanvas.LINETO;
 687         }
 688     }
 689     private void writePoly(double xPoints[], double yPoints[], int nPoints,
 690                            boolean close, byte command)
 691     {
 692         if (xPoints == null || yPoints == null) return;
 693         GrowableDataBuffer buf = getBuffer();
 694         buf.putByte(NGCanvas.PATHSTART);
 695         int pos = 0;
 696         byte polycmd = NGCanvas.MOVETO;
 697         for (int i = 0; i < nPoints; i++) {
 698             if (pos >= polybuf.length) {
 699                 flushPolyBuf(buf, polybuf, pos, polycmd);
 700                 pos = 0;
 701                 polycmd = NGCanvas.LINETO;
 702             }
 703             polybuf[pos++] = (float) xPoints[i];
 704             polybuf[pos++] = (float) yPoints[i];
 705         }
 706         flushPolyBuf(buf, polybuf, pos, polycmd);
 707         if (close) {
 708             buf.putByte(NGCanvas.CLOSEPATH);
 709         }
 710         buf.putByte(NGCanvas.PATHEND);
 711         buf.putByte(command);
 712         // Now that we have changed the PG layer path, we need to mark our path dirty.
 713         markPathDirty();
 714     }
 715 
 716     private void writeImage(Image img,
 717                             double dx, double dy, double dw, double dh)
 718     {
 719         if (img == null || img.getProgress() < 1.0) return;
 720         Object platformImg = img.impl_getPlatformImage();
 721         if (platformImg == null) return;
 722         updateTransform();
 723         GrowableDataBuffer buf = getBuffer();
 724         writeRectParams(buf, dx, dy, dw, dh, NGCanvas.DRAW_IMAGE);
 725         buf.putObject(platformImg);
 726     }
 727 
 728     private void writeImage(Image img,
 729                             double dx, double dy, double dw, double dh,
 730                             double sx, double sy, double sw, double sh)
 731     {
 732         if (img == null || img.getProgress() < 1.0) return;
 733         Object platformImg = img.impl_getPlatformImage();
 734         if (platformImg == null) return;
 735         updateTransform();
 736         GrowableDataBuffer buf = getBuffer();
 737         writeRectParams(buf, dx, dy, dw, dh, NGCanvas.DRAW_SUBIMAGE);
 738         buf.putFloat((float) sx);
 739         buf.putFloat((float) sy);
 740         buf.putFloat((float) sw);
 741         buf.putFloat((float) sh);
 742         buf.putObject(platformImg);
 743     }
 744 
 745     private void writeText(String text, double x, double y, double maxWidth,
 746                            byte command)
 747     {
 748         if (text == null) return;
 749         updateTransform();
 750         GrowableDataBuffer buf = getBuffer();
 751         buf.putByte(command);
 752         buf.putFloat((float) x);
 753         buf.putFloat((float) y);
 754         buf.putFloat((float) maxWidth);
 755         buf.putBoolean(theCanvas.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT);
 756         buf.putObject(text);
 757     }
 758 
 759     void writeParam(double v, byte command) {
 760         GrowableDataBuffer buf = getBuffer();
 761         buf.putByte(command);
 762         buf.putFloat((float) v);
 763     }
 764 
 765     private void writeParam(byte v, byte command) {
 766         GrowableDataBuffer buf = getBuffer();
 767         buf.putByte(command);
 768         buf.putByte(v);
 769     }
 770 
 771     private boolean txdirty;
 772     private void updateTransform() {
 773         if (txdirty) {
 774             txdirty = false;
 775             GrowableDataBuffer buf = getBuffer();
 776             buf.putByte(NGCanvas.TRANSFORM);
 777             buf.putDouble(curState.transform.getMxx());
 778             buf.putDouble(curState.transform.getMxy());
 779             buf.putDouble(curState.transform.getMxt());
 780             buf.putDouble(curState.transform.getMyx());
 781             buf.putDouble(curState.transform.getMyy());
 782             buf.putDouble(curState.transform.getMyt());
 783         }
 784     }
 785 
 786     void updateDimensions() {
 787         GrowableDataBuffer buf = getBuffer();
 788         buf.putByte(NGCanvas.SET_DIMS);
 789         buf.putFloat((float) theCanvas.getWidth());
 790         buf.putFloat((float) theCanvas.getHeight());
 791     }
 792 
 793     private void reset() {
 794         GrowableDataBuffer buf = getBuffer();
 795         // Only reset if we have a significant amount of data to omit,
 796         // this prevents a common occurence of "setFill(bg); fillRect();"
 797         // at the start of a session from invoking a reset.
 798         // But, do a reset anyway if the rendering layer has been falling
 799         // behind because that lets the synchronization step throw out the
 800         // older buffers that have been backing up.
 801         if (buf.writeValuePosition() > Canvas.DEFAULT_VAL_BUF_SIZE ||
 802             theCanvas.isRendererFallingBehind())
 803         {
 804             buf.reset();
 805             buf.putByte(NGCanvas.RESET);
 806             updateDimensions();
 807             txdirty = true;
 808             pathDirty = true;
 809             State s = this.curState;
 810             int numClipPaths = this.curState.numClipPaths;
 811             this.curState = new State();
 812             for (int i = 0; i < numClipPaths; i++) {
 813                 Path2D clip = clipStack.get(i);
 814                 buf.putByte(NGCanvas.PUSH_CLIP);
 815                 buf.putObject(clip);
 816             }
 817             this.curState.numClipPaths = numClipPaths;
 818             s.restore(this);
 819         }
 820     }
 821 
 822     private void resetIfCovers(Paint p, double x, double y, double w, double h) {
 823         Affine2D tx = this.curState.transform;
 824         if (tx.isTranslateOrIdentity()) {
 825             x += tx.getMxt();
 826             y += tx.getMyt();
 827             if (x > 0 || y > 0 ||
 828                 (x+w) < theCanvas.getWidth() ||
 829                 (y+h) < theCanvas.getHeight())
 830             {
 831                 return;
 832             }
 833         } else {
 834 //          quad test for coverage...?
 835             return;
 836         }
 837         if (p != null) {
 838             if (this.curState.blendop != BlendMode.SRC_OVER) return;
 839             if (!p.isOpaque() || this.curState.globalAlpha < 1.0) return;
 840         }
 841         if (this.curState.numClipPaths > 0) return;
 842         if (this.curState.effect != null) return;
 843         reset();
 844     }
 845 
 846     /**
 847     * Gets the {@code Canvas} that the {@code GraphicsContext} is issuing draw
 848     * commands to. There is only ever one {@code Canvas} for a 
 849     * {@code GraphicsContext}.
 850     *
 851     * @return Canvas the canvas that this {@code GraphicsContext} is issuing draw
 852     * commands to.
 853     */
 854     public Canvas getCanvas() {
 855         return theCanvas;
 856     } 
 857 
 858     /**
 859      * Saves the following attributes onto a stack.
 860      * <ul>
 861      *     <li>Global Alpha</li>
 862      *     <li>Global Blend Operation</li>
 863      *     <li>Transform</li>
 864      *     <li>Fill Paint</li>
 865      *     <li>Stroke Paint</li>
 866      *     <li>Line Width</li>
 867      *     <li>Line Cap</li>
 868      *     <li>Line Join</li>
 869      *     <li>Miter Limit</li>
 870      *     <li>Clip</li>
 871      *     <li>Font</li>
 872      *     <li>Text Align</li>
 873      *     <li>Text Baseline</li>
 874      *     <li>Effect</li>
 875      *     <li>Fill Rule</li>
 876      * </ul>
 877      * This method does NOT alter the current state in any way. Also, note that
 878      * the current path is not saved.
 879      */
 880     public void save() {
 881         stateStack.push(curState.copy());
 882     }
 883 
 884     /**
 885      * Pops the state off of the stack, setting the following attributes to their 
 886      * value at the time when that state was pushed onto the stack. If the stack
 887      * is empty then nothing is changed.
 888      * 
 889      * <ul>
 890      *     <li>Global Alpha</li>
 891      *     <li>Global Blend Operation</li>
 892      *     <li>Transform</li>
 893      *     <li>Fill Paint</li>
 894      *     <li>Stroke Paint</li>
 895      *     <li>Line Width</li>
 896      *     <li>Line Cap</li>
 897      *     <li>Line Join</li>
 898      *     <li>Miter Limit</li>
 899      *     <li>Clip</li>
 900      *     <li>Font</li>
 901      *     <li>Text Align</li>
 902      *     <li>Text Baseline</li>
 903      *     <li>Effect</li>
 904      *     <li>Fill Rule</li>
 905      * </ul>
 906      * Note that the current path is not restored.
 907      */
 908     public void restore() {
 909         if (!stateStack.isEmpty()) {
 910             State savedState = stateStack.pop();
 911             savedState.restore(this);
 912             txdirty = true;
 913         }
 914     }
 915 
 916     /**
 917      * Translates the current transform by x, y.
 918      * @param x value to translate along the x axis.
 919      * @param y value to translate along the y axis.
 920      */
 921     public void translate(double x, double y) {
 922         curState.transform.translate(x, y);
 923         txdirty = true;
 924     }
 925 
 926     /**
 927      * Scales the current transform by x, y.
 928      * @param x value to scale in the x axis.
 929      * @param y value to scale in the y axis.
 930      */
 931     public void scale(double x, double y) {
 932         curState.transform.scale(x, y);
 933         txdirty = true;
 934     }
 935 
 936     /**
 937      * Rotates the current transform in degrees.
 938      * @param degrees value in degrees to rotate the current transform.
 939      */
 940     public void rotate(double degrees) {
 941         curState.transform.rotate(Math.toRadians(degrees));
 942         txdirty = true;
 943     }
 944 
 945     /**
 946      * Concatenates the input with the current transform.
 947      * 
 948      * @param mxx - the X coordinate scaling element of the 3x4 matrix
 949      * @param myx - the Y coordinate shearing element of the 3x4 matrix
 950      * @param mxy - the X coordinate shearing element of the 3x4 matrix
 951      * @param myy - the Y coordinate scaling element of the 3x4 matrix
 952      * @param mxt - the X coordinate translation element of the 3x4 matrix
 953      * @param myt - the Y coordinate translation element of the 3x4 matrix
 954      */
 955     public void transform(double mxx, double myx,
 956                           double mxy, double myy,
 957                           double mxt, double myt)
 958     {
 959         curState.transform.concatenate(mxx, mxy, mxt,
 960                                        myx, myy, myt);
 961         txdirty = true;
 962     }
 963     
 964     /**
 965      * Concatenates the input with the current transform. Only 2D transforms are
 966      * supported. The only values used are the X and Y scaling, translation, and
 967      * shearing components of a transform. A {@code null} value is treated as identity.
 968      * 
 969      * @param xform The affine to be concatenated with the current transform or null.
 970      */
 971     public void transform(Affine xform) {
 972         if (xform == null) return;
 973         curState.transform.concatenate(xform.getMxx(), xform.getMxy(), xform.getTx(),
 974                                        xform.getMyx(), xform.getMyy(), xform.getTy());
 975         txdirty = true;
 976     }
 977 
 978     /**
 979      * Sets the current transform.
 980      * @param mxx - the X coordinate scaling element of the 3x4 matrix
 981      * @param myx - the Y coordinate shearing element of the 3x4 matrix
 982      * @param mxy - the X coordinate shearing element of the 3x4 matrix
 983      * @param myy - the Y coordinate scaling element of the 3x4 matrix
 984      * @param mxt - the X coordinate translation element of the 3x4 matrix
 985      * @param myt - the Y coordinate translation element of the 3x4 matrix 
 986      */
 987     public void setTransform(double mxx, double myx,
 988                              double mxy, double myy,
 989                              double mxt, double myt)
 990     {
 991         curState.transform.setTransform(mxx, myx,
 992                                         mxy, myy,
 993                                         mxt, myt);
 994         txdirty = true;
 995     }
 996     
 997     /**
 998      * Sets the current transform. Only 2D transforms are supported. The only 
 999      * values used are the X and Y scaling, translation, and shearing components
1000      * of a transform.
1001      * 
1002      * @param xform The affine to be copied and used as the current transform.
1003      */
1004     public void setTransform(Affine xform) {
1005         curState.transform.setTransform(xform.getMxx(), xform.getMyx(),
1006                                         xform.getMxy(), xform.getMyy(),
1007                                         xform.getTx(), xform.getTy());
1008         txdirty = true;
1009     }
1010     
1011     /**
1012      * Copies the current transform into the supplied object, creating
1013      * a new {@link Affine} object if it is null, and returns the object
1014      * containing the copy.
1015      * 
1016      * @param xform A transform object that will be used to hold the result.
1017      * If xform is non null, then this method will copy the current transform 
1018      * into that object. If xform is null a new transform object will be
1019      * constructed. In either case, the return value is a copy of the current 
1020      * transform. 
1021      *
1022      * @return A copy of the current transform.  
1023      */
1024     public Affine getTransform(Affine xform) {
1025         if (xform == null) {
1026             xform = new Affine();
1027         }
1028         
1029         xform.setMxx(curState.transform.getMxx());
1030         xform.setMxy(curState.transform.getMxy());
1031         xform.setMxz(0);
1032         xform.setTx(curState.transform.getMxt());
1033         xform.setMyx(curState.transform.getMyx());
1034         xform.setMyy(curState.transform.getMyy());
1035         xform.setMyz(0);
1036         xform.setTy(curState.transform.getMyt());
1037         xform.setMzx(0);
1038         xform.setMzy(0);
1039         xform.setMzz(1);
1040         xform.setTz(0);
1041         
1042         return xform;
1043     }
1044     
1045     /**
1046      * Returns a copy of the current transform.
1047      * 
1048      * @return a copy of the transform of the current state. 
1049      */
1050     public Affine getTransform() {
1051         return getTransform(null);
1052     }
1053 
1054     /**
1055      * Sets the global alpha of the current state.
1056      * The default value is {@code 1.0}.
1057      * Any valid double can be set, but only values in the range
1058      * {@code [0.0, 1.0]} are valid and the nearest value in that
1059      * range will be used for rendering.
1060      * The global alpha is a <a href="#comm-attr">common attribute</a>
1061      * used for nearly all rendering methods as specified in the
1062      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1063      * 
1064      * @param alpha the new alpha value, clamped to {@code [0.0, 1.0]}
1065      *              during actual use.
1066      */
1067     public void setGlobalAlpha(double alpha) {
1068         if (curState.globalAlpha != alpha) {
1069             curState.globalAlpha = alpha;
1070             alpha = (alpha > 1.0) ? 1.0 : (alpha < 0.0) ? 0.0 : alpha;
1071             writeParam(alpha, NGCanvas.GLOBAL_ALPHA);
1072         }
1073     }
1074     
1075     /**
1076      * Gets the current global alpha.
1077      * The default value is {@code 1.0}.
1078      * The global alpha is a <a href="#comm-attr">common attribute</a>
1079      * used for nearly all rendering methods as specified in the
1080      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1081      * 
1082      * @return the current global alpha. 
1083      */
1084     public double getGlobalAlpha() {
1085         return curState.globalAlpha;
1086     }
1087 
1088     /**
1089      * Sets the global blend mode.
1090      * The default value is {@link BlendMode#SRC_OVER SRC_OVER}.
1091      * A {@code null} value will be ignored and the current value will remain unchanged.
1092      * The blend mode is a <a href="#comm-attr">common attribute</a>
1093      * used for nearly all rendering methods as specified in the
1094      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1095      * 
1096      * @param op the {@code BlendMode} that will be set or null.
1097      */
1098     public void setGlobalBlendMode(BlendMode op) {
1099         if (op != null && op != curState.blendop) {
1100             GrowableDataBuffer buf = getBuffer();           
1101             curState.blendop = op; 
1102             buf.putByte(NGCanvas.COMP_MODE);
1103             buf.putObject(Blend.impl_getToolkitMode(op));
1104         }
1105     }
1106     
1107     /**
1108      * Gets the global blend mode.
1109      * The default value is {@link BlendMode#SRC_OVER SRC_OVER}.
1110      * The blend mode is a <a href="#comm-attr">common attribute</a>
1111      * used for nearly all rendering methods as specified in the
1112      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1113      * 
1114      * @return the global {@code BlendMode} of the current state.
1115      */
1116     public BlendMode getGlobalBlendMode() {
1117         return curState.blendop;
1118     }
1119 
1120     /**
1121      * Sets the current fill paint attribute.
1122      * The default value is {@link Color#BLACK BLACK}.
1123      * The fill paint is a <a href="#fill-attr">fill attribute</a>
1124      * used for any of the fill methods as specified in the
1125      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1126      * A {@code null} value will be ignored and the current value will remain unchanged.
1127      * 
1128      * @param p The {@code Paint} to be used as the fill {@code Paint} or null.
1129      */
1130     public void setFill(Paint p) {
1131         if (p != null && curState.fill != p) {
1132             curState.fill = p;
1133             writePaint(p, NGCanvas.FILL_PAINT);
1134         }
1135     }
1136     
1137     /**
1138      * Gets the current fill paint attribute.
1139      * The default value is {@link Color#BLACK BLACK}.
1140      * The fill paint is a <a href="#fill-attr">fill attribute</a>
1141      * used for any of the fill methods as specified in the
1142      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1143      * 
1144      * @return p The {@code Paint} to be used as the fill {@code Paint}.
1145      */
1146     public Paint getFill() {
1147         return curState.fill;
1148     }
1149 
1150     /**
1151      * Sets the current stroke paint attribute.
1152      * The default value is {@link Color#BLACK BLACK}.
1153      * The stroke paint is a <a href="#strk-attr">stroke attribute</a>
1154      * used for any of the stroke methods as specified in the
1155      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1156      * A {@code null} value will be ignored and the current value will remain unchanged.
1157      * 
1158      * @param p The Paint to be used as the stroke Paint or null.
1159      */
1160     public void setStroke(Paint p) {
1161         if (p != null && curState.stroke != p) {
1162             curState.stroke = p;
1163             writePaint(p, NGCanvas.STROKE_PAINT);
1164         }
1165     }
1166     
1167     /**
1168      * Gets the current stroke.
1169      * The default value is {@link Color#BLACK BLACK}.
1170      * The stroke paint is a <a href="#strk-attr">stroke attribute</a>
1171      * used for any of the stroke methods as specified in the
1172      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1173      * 
1174      * @return the {@code Paint} to be used as the stroke {@code Paint}.
1175      */
1176     public Paint getStroke() {
1177         return curState.stroke;
1178     }
1179 
1180     /**
1181      * Sets the current line width.
1182      * The default value is {@code 1.0}.
1183      * The line width is a <a href="#strk-attr">stroke attribute</a>
1184      * used for any of the stroke methods as specified in the
1185      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1186      * An infinite or non-positive value outside of the range {@code (0, +inf)}
1187      * will be ignored and the current value will remain unchanged.
1188      * 
1189      * @param lw value in the range {0-positive infinity}, with any other value 
1190      * being ignored and leaving the value unchanged.
1191      */
1192     public void setLineWidth(double lw) {
1193         // Per W3C spec: On setting, zero, negative, infinite, and NaN
1194         // values must be ignored, leaving the value unchanged
1195         if (lw > 0 && lw < Double.POSITIVE_INFINITY) {
1196             if (curState.linewidth != lw) {
1197                 curState.linewidth = lw;
1198                 writeParam(lw, NGCanvas.LINE_WIDTH);
1199             }
1200         }
1201     }
1202     
1203     /**
1204      * Gets the current line width.
1205      * The default value is {@code 1.0}.
1206      * The line width is a <a href="#strk-attr">stroke attribute</a>
1207      * used for any of the stroke methods as specified in the
1208      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1209      * 
1210      * @return value between 0 and infinity.
1211      */
1212     public double getLineWidth() {
1213         return curState.linewidth;
1214     }
1215 
1216     /**
1217      * Sets the current stroke line cap.
1218      * The default value is {@link StrokeLineCap#SQUARE SQUARE}.
1219      * The line cap is a <a href="#strk-attr">stroke attribute</a>
1220      * used for any of the stroke methods as specified in the
1221      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1222      * A {@code null} value will be ignored and the current value will remain unchanged.
1223      * 
1224      * @param cap {@code StrokeLineCap} with a value of Butt, Round, or Square or null.
1225      */
1226     public void setLineCap(StrokeLineCap cap) {
1227         if (cap != null && curState.linecap != cap) {
1228             byte v;
1229             switch (cap) {
1230                 case BUTT: v = NGCanvas.CAP_BUTT; break;
1231                 case ROUND: v = NGCanvas.CAP_ROUND; break;
1232                 case SQUARE: v = NGCanvas.CAP_SQUARE; break;
1233                 default: return;
1234             }
1235             curState.linecap = cap;
1236             writeParam(v, NGCanvas.LINE_CAP);
1237         }
1238     }
1239     
1240     /**
1241      * Gets the current stroke line cap. 
1242      * The default value is {@link StrokeLineCap#SQUARE SQUARE}.
1243      * The line cap is a <a href="#strk-attr">stroke attribute</a>
1244      * used for any of the stroke methods as specified in the
1245      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1246      * 
1247      * @return {@code StrokeLineCap} with a value of Butt, Round, or Square.
1248      */
1249     public StrokeLineCap getLineCap() {
1250         return curState.linecap;
1251     }
1252 
1253     /**
1254      * Sets the current stroke line join.
1255      * The default value is {@link StrokeLineJoin#MITER}.
1256      * The line join is a <a href="#strk-attr">stroke attribute</a>
1257      * used for any of the stroke methods as specified in the
1258      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1259      * A {@code null} value will be ignored and the current value will remain unchanged.
1260      * 
1261      * @param join {@code StrokeLineJoin} with a value of Miter, Bevel, or Round or null.
1262      */
1263     public void setLineJoin(StrokeLineJoin join) {
1264         if (join != null && curState.linejoin != join) {
1265             byte v;
1266             switch (join) {
1267                 case MITER: v = NGCanvas.JOIN_MITER; break;
1268                 case BEVEL: v = NGCanvas.JOIN_BEVEL; break;
1269                 case ROUND: v = NGCanvas.JOIN_ROUND; break;
1270                 default: return;
1271             }
1272             curState.linejoin = join;
1273             writeParam(v, NGCanvas.LINE_JOIN);
1274         }
1275     }
1276     
1277     /**
1278      * Gets the current stroke line join.
1279      * The default value is {@link StrokeLineJoin#MITER}.
1280      * The line join is a <a href="#strk-attr">stroke attribute</a>
1281      * used for any of the stroke methods as specified in the
1282      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1283      * 
1284      * @return {@code StrokeLineJoin} with a value of Miter, Bevel, or Round.
1285      */
1286     public StrokeLineJoin getLineJoin() {
1287         return curState.linejoin;
1288     }
1289 
1290     /**
1291      * Sets the current miter limit.
1292      * The default value is {@code 10.0}.
1293      * The miter limit is a <a href="#strk-attr">stroke attribute</a>
1294      * used for any of the stroke methods as specified in the
1295      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1296      * An infinite or non-positive value outside of the range {@code (0, +inf)}
1297      * will be ignored and the current value will remain unchanged.
1298      * 
1299      * @param ml miter limit value between 0 and positive infinity with 
1300      * any other value being ignored and leaving the value unchanged.
1301      */
1302     public void setMiterLimit(double ml) {
1303         // Per W3C spec: On setting, zero, negative, infinite, and NaN
1304         // values must be ignored, leaving the value unchanged
1305         if (ml > 0.0 && ml < Double.POSITIVE_INFINITY) {
1306             if (curState.miterlimit != ml) {
1307                 curState.miterlimit = ml;
1308                 writeParam(ml, NGCanvas.MITER_LIMIT);
1309             }
1310         }
1311     }
1312     
1313     /**
1314      * Gets the current miter limit.
1315      * The default value is {@code 10.0}.
1316      * The miter limit is a <a href="#strk-attr">stroke attribute</a>
1317      * used for any of the stroke methods as specified in the
1318      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1319      * 
1320      * @return the miter limit value in the range {@code 0.0-positive infinity}
1321      */
1322     public double getMiterLimit() {
1323         return curState.miterlimit;
1324     }
1325 
1326     /**
1327      * Sets the current Font.
1328      * The default value is specified by {@link Font#getDefault()}.
1329      * The font is a <a href="#text-attr">text attribute</a>
1330      * used for any of the text methods as specified in the
1331      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1332      * A {@code null} value will be ignored and the current value will remain unchanged.
1333      * 
1334      * @param f the Font or null.
1335      */
1336     public void setFont(Font f) {
1337         if (f != null && curState.font != f) {
1338             curState.font = f;
1339             GrowableDataBuffer buf = getBuffer();
1340             buf.putByte(NGCanvas.FONT);
1341             buf.putObject(f.impl_getNativeFont());
1342         }
1343     }
1344     
1345     /**
1346      * Gets the current Font.
1347      * The default value is specified by {@link Font#getDefault()}.
1348      * The font is a <a href="#text-attr">text attribute</a>
1349      * used for any of the text methods as specified in the
1350      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1351      * 
1352      * @return the Font 
1353      */
1354     public Font getFont() {
1355         return curState.font;
1356     }
1357 
1358     /**
1359      * Defines horizontal text alignment, relative to the text {@code x} origin.
1360      * The default value is {@link TextAlignment#LEFT LEFT}.
1361      * The text alignment is a <a href="#text-attr">text attribute</a>
1362      * used for any of the text methods as specified in the
1363      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1364      * <p>
1365      * Let horizontal bounds represent the logical width of a single line of
1366      * text. Where each line of text has a separate horizontal bounds.
1367      * <p>
1368      * Then TextAlignment is specified as:
1369      * <ul>
1370      * <li>Left: the left edge of the horizontal bounds will be at {@code x}.
1371      * <li>Center: the center, halfway between left and right edge, of the 
1372      * horizontal bounds will be at {@code x}.
1373      * <li>Right: the right edge of the horizontal bounds will be at {@code x}.
1374      * </ul>
1375      * <p>
1376      *
1377      * Note: Canvas does not support line wrapping, therefore the text
1378      * alignment Justify is identical to left aligned text.
1379      * <p>
1380      * A {@code null} value will be ignored and the current value will remain unchanged.
1381      * 
1382      * @param align {@code TextAlignment} with values of Left, Center, Right or null.
1383      */
1384     public void setTextAlign(TextAlignment align) {
1385         if (align != null && curState.textalign != align) {
1386             byte a;
1387             switch (align) {
1388                 case LEFT: a = NGCanvas.ALIGN_LEFT; break;
1389                 case CENTER: a = NGCanvas.ALIGN_CENTER; break;
1390                 case RIGHT: a = NGCanvas.ALIGN_RIGHT; break;
1391                 case JUSTIFY: a = NGCanvas.ALIGN_JUSTIFY; break;
1392                 default: return;
1393             }
1394             curState.textalign = align;
1395             writeParam(a, NGCanvas.TEXT_ALIGN);
1396         }
1397     }
1398     
1399     /**
1400      * Gets the current {@code TextAlignment}.
1401      * The default value is {@link TextAlignment#LEFT LEFT}.
1402      * The text alignment is a <a href="#text-attr">text attribute</a>
1403      * used for any of the text methods as specified in the
1404      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1405      * 
1406      * @return {@code TextAlignment} with values of Left, Center, Right, or
1407      * Justify.
1408      */
1409     public TextAlignment getTextAlign() {
1410         return curState.textalign;
1411     }
1412 
1413     /**
1414      * Sets the current Text Baseline.
1415      * The default value is {@link VPos#BASELINE BASELINE}.
1416      * The text baseline is a <a href="#text-attr">text attribute</a>
1417      * used for any of the text methods as specified in the
1418      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1419      * A {@code null} value will be ignored and the current value will remain unchanged.
1420      * 
1421      * @param baseline {@code VPos} with values of Top, Center, Baseline, or Bottom or null.
1422      */
1423     public void setTextBaseline(VPos baseline) {
1424         if (baseline != null && curState.textbaseline != baseline) {
1425             byte b;
1426             switch (baseline) {
1427                 case TOP: b = NGCanvas.BASE_TOP; break;
1428                 case CENTER: b = NGCanvas.BASE_MIDDLE; break;
1429                 case BASELINE: b = NGCanvas.BASE_ALPHABETIC; break;
1430                 case BOTTOM: b = NGCanvas.BASE_BOTTOM; break;
1431                 default: return;
1432             }
1433             curState.textbaseline = baseline;
1434             writeParam(b, NGCanvas.TEXT_BASELINE);
1435         }
1436     }
1437     
1438     /**
1439      * Gets the current Text Baseline.
1440      * The default value is {@link VPos#BASELINE BASELINE}.
1441      * The text baseline is a <a href="#text-attr">text attribute</a>
1442      * used for any of the text methods as specified in the
1443      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1444      * 
1445      * @return {@code VPos} with values of Top, Center, Baseline, or Bottom
1446      */
1447     public VPos getTextBaseline() {
1448         return curState.textbaseline;
1449     }
1450 
1451     /**
1452      * Fills the given string of text at position x, y
1453      * with the current fill paint attribute.
1454      * A {@code null} text value will be ignored.
1455      * <p>
1456      * This method will be affected by any of the
1457      * <a href="#comm-attr">global common</a>,
1458      * <a href="#fill-attr">fill</a>,
1459      * or <a href="#text-attr">text</a>
1460      * attributes as specified in the
1461      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1462      * </p>
1463      * 
1464      * @param text the string of text or null.
1465      * @param x position on the x axis.
1466      * @param y position on the y axis.
1467      */
1468     public void fillText(String text, double x, double y) {
1469         writeText(text, x, y, 0, NGCanvas.FILL_TEXT);
1470     }
1471 
1472     /**
1473      * Draws the given string of text at position x, y
1474      * with the current stroke paint attribute.
1475      * A {@code null} text value will be ignored.
1476      * <p>
1477      * This method will be affected by any of the
1478      * <a href="#comm-attr">global common</a>,
1479      * <a href="#strk-attr">stroke</a>,
1480      * or <a href="#text-attr">text</a>
1481      * attributes as specified in the
1482      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1483      * </p>
1484      * 
1485      * @param text the string of text or null.
1486      * @param x position on the x axis.
1487      * @param y position on the y axis.
1488      */
1489     public void strokeText(String text, double x, double y) {
1490         writeText(text, x, y, 0, NGCanvas.STROKE_TEXT);
1491     }
1492 
1493     /**
1494      * Fills text and includes a maximum width of the string. 
1495      * If the width of the text extends past max width, then it will be sized
1496      * to fit.
1497      * A {@code null} text value will be ignored.
1498      * <p>
1499      * This method will be affected by any of the
1500      * <a href="#comm-attr">global common</a>,
1501      * <a href="#fill-attr">fill</a>,
1502      * or <a href="#text-attr">text</a>
1503      * attributes as specified in the
1504      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1505      * </p>
1506      * 
1507      * @param text the string of text or null.
1508      * @param x position on the x axis.
1509      * @param y position on the y axis.
1510      * @param maxWidth  maximum width the text string can have.
1511      */
1512     public void fillText(String text, double x, double y, double maxWidth) {
1513         if (maxWidth <= 0) return;
1514         writeText(text, x, y, maxWidth, NGCanvas.FILL_TEXT);
1515     }
1516 
1517     /**
1518      * Draws text with stroke paint and includes a maximum width of the string.
1519      * If the width of the text extends past max width, then it will be sized
1520      * to fit.
1521      * A {@code null} text value will be ignored.
1522      * <p>
1523      * This method will be affected by any of the
1524      * <a href="#comm-attr">global common</a>,
1525      * <a href="#strk-attr">stroke</a>,
1526      * or <a href="#text-attr">text</a>
1527      * attributes as specified in the
1528      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1529      * </p>
1530      * 
1531      * @param text the string of text or null.
1532      * @param x position on the x axis.
1533      * @param y position on the y axis.
1534      * @param maxWidth  maximum width the text string can have.
1535      */
1536     public void strokeText(String text, double x, double y, double maxWidth) {
1537         if (maxWidth <= 0) return;
1538         writeText(text, x, y, maxWidth, NGCanvas.STROKE_TEXT);
1539     }
1540 
1541 
1542     /**
1543      * Set the filling rule attribute for determining the interior of paths
1544      * in fill or clip operations.
1545      * The default value is {@code FillRule.NON_ZERO}.
1546      * A {@code null} value will be ignored and the current value will remain unchanged.
1547      * The fill rule is a <a href="#path-attr">path attribute</a>
1548      * used for any of the fill or clip path methods as specified in the
1549      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1550      *
1551      * @param fillRule {@code FillRule} with a value of  Even_odd or Non_zero or null.
1552      */
1553      public void setFillRule(FillRule fillRule) {
1554          if (fillRule != null && curState.fillRule != fillRule) {
1555             byte b;
1556             if (fillRule == FillRule.EVEN_ODD) {
1557                 b = NGCanvas.FILL_RULE_EVEN_ODD;
1558             } else { 
1559                 b = NGCanvas.FILL_RULE_NON_ZERO;
1560             }
1561             curState.fillRule = fillRule;
1562             writeParam(b, NGCanvas.FILL_RULE);
1563         }
1564      }
1565     
1566     /**
1567      * Get the filling rule attribute for determining the interior of paths
1568      * in fill and clip operations.
1569      * The default value is {@code FillRule.NON_ZERO}.
1570      * The fill rule is a <a href="#path-attr">path attribute</a>
1571      * used for any of the fill or clip path methods as specified in the
1572      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
1573      *
1574      * @return current fill rule.
1575      */
1576      public FillRule getFillRule() {
1577          return curState.fillRule;
1578      }
1579 
1580     /**
1581      * Resets the current path to empty.
1582      * The default path is empty.
1583      * The current path is a <a href="#path-attr">path attribute</a>
1584      * used for any of the path methods as specified in the
1585      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1586      * and <b>is not affected</b> by the {@link #save()} and
1587      * {@link #restore()} operations.
1588      */
1589     public void beginPath() {
1590         path.reset();
1591         markPathDirty();
1592     }
1593 
1594     /**
1595      * Issues a move command for the current path to the given x,y coordinate.
1596      * The coordinates are transformed by the current transform as they are
1597      * added to the path and unaffected by subsequent changes to the transform.
1598      * The current path is a <a href="#path-attr">path attribute</a>
1599      * used for any of the path methods as specified in the
1600      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1601      * and <b>is not affected</b> by the {@link #save()} and
1602      * {@link #restore()} operations.
1603      * 
1604      * @param x0 the X position for the move to command.
1605      * @param y0 the Y position for the move to command.
1606      */
1607     public void moveTo(double x0, double y0) {
1608         coords[0] = (float) x0;
1609         coords[1] = (float) y0;
1610         curState.transform.transform(coords, 0, coords, 0, 1);
1611         path.moveTo(coords[0], coords[1]);
1612         markPathDirty();
1613     }
1614 
1615     /**
1616      * Adds segments to the current path to make a line to the given x,y
1617      * coordinate.
1618      * The coordinates are transformed by the current transform as they are
1619      * added to the path and unaffected by subsequent changes to the transform.
1620      * The current path is a <a href="#path-attr">path attribute</a>
1621      * used for any of the path methods as specified in the
1622      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1623      * and <b>is not affected</b> by the {@link #save()} and
1624      * {@link #restore()} operations.
1625      * 
1626      * @param x1 the X coordinate of the ending point of the line.
1627      * @param y1 the Y coordinate of the ending point of the line.
1628      */
1629     public void lineTo(double x1, double y1) {
1630         coords[0] = (float) x1;
1631         coords[1] = (float) y1;
1632         curState.transform.transform(coords, 0, coords, 0, 1);
1633         if (path.getNumCommands() == 0) {
1634             path.moveTo(coords[0], coords[1]);
1635         }
1636         path.lineTo(coords[0], coords[1]);
1637         markPathDirty();
1638     }
1639 
1640     /**
1641      * Adds segments to the current path to make a quadratic Bezier curve.
1642      * The coordinates are transformed by the current transform as they are
1643      * added to the path and unaffected by subsequent changes to the transform.
1644      * The current path is a <a href="#path-attr">path attribute</a>
1645      * used for any of the path methods as specified in the
1646      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1647      * and <b>is not affected</b> by the {@link #save()} and
1648      * {@link #restore()} operations.
1649      * 
1650      * @param xc the X coordinate of the control point
1651      * @param yc the Y coordinate of the control point
1652      * @param x1 the X coordinate of the end point
1653      * @param y1 the Y coordinate of the end point
1654      */
1655     public void quadraticCurveTo(double xc, double yc, double x1, double y1) {
1656         coords[0] = (float) xc;
1657         coords[1] = (float) yc;
1658         coords[2] = (float) x1;
1659         coords[3] = (float) y1;
1660         curState.transform.transform(coords, 0, coords, 0, 2);
1661         if (path.getNumCommands() == 0) {
1662             path.moveTo(coords[0], coords[1]);
1663         }
1664         path.quadTo(coords[0], coords[1], coords[2], coords[3]);
1665         markPathDirty();
1666     }
1667 
1668     /**
1669      * Adds segments to the current path to make a cubic Bezier curve.
1670      * The coordinates are transformed by the current transform as they are
1671      * added to the path and unaffected by subsequent changes to the transform.
1672      * The current path is a <a href="#path-attr">path attribute</a>
1673      * used for any of the path methods as specified in the
1674      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1675      * and <b>is not affected</b> by the {@link #save()} and
1676      * {@link #restore()} operations.
1677      * 
1678      * @param xc1 the X coordinate of first Bezier control point.
1679      * @param yc1 the Y coordinate of the first Bezier control point.
1680      * @param xc2 the X coordinate of the second Bezier control point.
1681      * @param yc2 the Y coordinate of the second Bezier control point.
1682      * @param x1  the X coordinate of the end point.
1683      * @param y1  the Y coordinate of the end point.
1684      */
1685     public void bezierCurveTo(double xc1, double yc1, double xc2, double yc2, double x1, double y1) {
1686         coords[0] = (float) xc1;
1687         coords[1] = (float) yc1;
1688         coords[2] = (float) xc2;
1689         coords[3] = (float) yc2;
1690         coords[4] = (float) x1;
1691         coords[5] = (float) y1;
1692         curState.transform.transform(coords, 0, coords, 0, 3);
1693         if (path.getNumCommands() == 0) {
1694             path.moveTo(coords[0], coords[1]);
1695         }
1696         path.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
1697         markPathDirty();
1698     }
1699 
1700     /**
1701      * Adds segments to the current path to make an arc.
1702      * The coordinates are transformed by the current transform as they are
1703      * added to the path and unaffected by subsequent changes to the transform.
1704      * The current path is a <a href="#path-attr">path attribute</a>
1705      * used for any of the path methods as specified in the
1706      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1707      * and <b>is not affected</b> by the {@link #save()} and
1708      * {@link #restore()} operations.
1709      * <p>
1710      * If {@code p0} is the current point in the path and {@code p1} is the
1711      * point specified by {@code (x1, y1)} and {@code p2} is the point
1712      * specified by {@code (x2, y2)}, then the arc segments appended will
1713      * be segments along the circumference of a circle of the specified
1714      * radius touching and inscribed into the convex (interior) side of
1715      * {@code p0->p1->p2}.  The path will contain a line segment (if
1716      * needed) to the tangent point between that circle and {@code p0->p1}
1717      * followed by circular arc segments to reach the tangent point between
1718      * the circle and {@code p1->p2} and will end with the current point at
1719      * that tangent point (not at {@code p2}).
1720      * Note that the radius and circularity of the arc segments will be
1721      * measured or considered relative to the current transform, but the
1722      * resulting segments that are computed from those untransformed
1723      * points will then be transformed when they are added to the path.
1724      * Since all computation is done in untransformed space, but the
1725      * pre-existing path segments are all transformed, the ability to
1726      * correctly perform the computation may implicitly depend on being
1727      * able to inverse transform the current end of the current path back
1728      * into untransformed coordinates.
1729      * </p>
1730      * <p>
1731      * If there is no way to compute and inscribe the indicated circle
1732      * for any reason then the entire operation will simply append segments
1733      * to force a line to point {@code p1}.  Possible reasons that the
1734      * computation may fail include:
1735      * <ul>
1736      * <li>The current path is empty.</li>
1737      * <li>The segments {@code p0->p1->p2} are colinear.</li>
1738      * <li>the current transform is non-invertible so that the current end
1739      * point of the current path cannot be untransformed for computation.</li>
1740      * </ul>
1741      * </p>
1742      * 
1743      * @param x1 the X coordinate of the first point of the arc.
1744      * @param y1 the Y coordinate of the first point of the arc.
1745      * @param x2 the X coordinate of the second point of the arc.
1746      * @param y2 the Y coordinate of the second point of the arc.
1747      * @param radius the radius of the arc in the range {0.0-positive infinity}.
1748      */
1749     public void arcTo(double x1, double y1, double x2, double y2, double radius) {
1750         if (path.getNumCommands() == 0) {
1751             moveTo(x1, y1);
1752             lineTo(x1, y1);
1753         } else if (!tryArcTo((float) x1, (float) y1, (float) x2, (float) y2,
1754                              (float) radius))
1755         {
1756             lineTo(x1, y1);
1757         }
1758     }
1759 
1760     private static double lenSq(double x0, double y0, double x1, double y1) {
1761         x1 -= x0;
1762         y1 -= y0;
1763         return x1 * x1 + y1 * y1;
1764     }
1765 
1766     private boolean tryArcTo(float x1, float y1, float x2, float y2, float radius) {
1767         float x0, y0;
1768         if (curState.transform.isTranslateOrIdentity()) {
1769             x0 = (float) (path.getCurrentX() - curState.transform.getMxt());
1770             y0 = (float) (path.getCurrentY() - curState.transform.getMyt());
1771         } else {
1772             coords[0] = path.getCurrentX();
1773             coords[1] = path.getCurrentY();
1774             try {
1775                 curState.transform.inverseTransform(coords, 0, coords, 0, 1);
1776             } catch (NoninvertibleTransformException e) {
1777                 return false;
1778             }
1779             x0 = coords[0];
1780             y0 = coords[1];
1781         }
1782         // call x1,y1 the corner point
1783         // If 2*theta is the angle described by p0->p1->p2
1784         // then theta is the angle described by p0->p1->centerpt and
1785         // centerpt->p1->p2
1786         // We know that the distance from the arc center to the tangent points
1787         // is r, and if A is the distance from the corner to the tangent point
1788         // then we know:
1789         // tan(theta) = r/A
1790         // A = r / sin(theta)
1791         // B = A * cos(theta) = r * (sin/cos) = r * tan
1792         // We use the cosine rule on the triangle to get the 2*theta angle:
1793         // cosB = (a^2 + c^2 - b^2) / (2ac)
1794         // where a and c are the adjacent sides and b is the opposite side
1795         // i.e. a = p0->p1, c=p1->p2, b=p0->p2
1796         // Then we can use the tan^2 identity to compute B:
1797         // tan^2 = (1 - cos(2theta)) / (1 + cos(2theta))
1798         double lsq01 = lenSq(x0, y0, x1, y1);
1799         double lsq12 = lenSq(x1, y1, x2, y2);
1800         double lsq02 = lenSq(x0, y0, x2, y2);
1801         double len01 = Math.sqrt(lsq01);
1802         double len12 = Math.sqrt(lsq12);
1803         double cosnum = lsq01 + lsq12 - lsq02;
1804         double cosden = 2.0 * len01 * len12;
1805         if (cosden == 0.0 || radius <= 0f) {
1806             return false;
1807         }
1808         double cos_2theta = cosnum / cosden;
1809         double tansq_den = (1.0 + cos_2theta);
1810         if (tansq_den == 0.0) {
1811             return false;
1812         }
1813         double tansq_theta = (1.0 - cos_2theta) / tansq_den;
1814         double A = radius / Math.sqrt(tansq_theta);
1815         double tx0 = x1 + (A / len01) * (x0 - x1);
1816         double ty0 = y1 + (A / len01) * (y0 - y1);
1817         double tx1 = x1 + (A / len12) * (x2 - x1);
1818         double ty1 = y1 + (A / len12) * (y2 - y1);
1819         // The midpoint between the two tangent points
1820         double mx = (tx0 + tx1) / 2.0;
1821         double my = (ty0 + ty1) / 2.0;
1822         // similar triangles tell us that:
1823         // len(m,center)/len(m,tangent) = len(m,tangent)/len(corner,m)
1824         // len(m,center) = lensq(m,tangent)/len(corner,m)
1825         // center = m + (m - p1) * len(m,center) / len(corner,m)
1826         //   = m + (m - p1) * (lensq(m,tangent) / lensq(corner,m))
1827         double lenratioden = lenSq(mx, my, x1, y1);
1828         if (lenratioden == 0.0) {
1829             return false;
1830         }
1831         double lenratio = lenSq(mx, my, tx0, ty0) / lenratioden;
1832         double cx = mx + (mx - x1) * lenratio;
1833         double cy = my + (my - y1) * lenratio;
1834         if (!(cx == cx && cy == cy)) {
1835             return false;
1836         }
1837         // Looks like we are good to draw, first we have to get to the
1838         // initial tangent point with a line segment.
1839         if (tx0 != x0 || ty0 != y0) {
1840             lineTo(tx0, ty0);
1841         }
1842         // We need sin(arc/2), cos(arc/2)
1843         // and possibly sin(arc/4), cos(arc/4) if we need 2 cubic beziers
1844         // We have tan(theta) = tan(tri/2)
1845         // arc = 180-tri
1846         // arc/2 = (180-tri)/2 = 90-(tri/2)
1847         // sin(arc/2) = sin(90-(tri/2)) = cos(tri/2)
1848         // cos(arc/2) = cos(90-(tri/2)) = sin(tri/2)
1849         // 2theta = tri, therefore theta = tri/2
1850         // cos(tri/2)^2 = (1+cos(tri)) / 2.0 = (1+cos_2theta)/2.0
1851         // sin(tri/2)^2 = (1-cos(tri)) / 2.0 = (1-cos_2theta)/2.0
1852         // sin(arc/2) = cos(tri/2) = sqrt((1+cos_2theta)/2.0)
1853         // cos(arc/2) = sin(tri/2) = sqrt((1-cos_2theta)/2.0)
1854         // We compute cos(arc/2) here as we need it in either case below
1855         double coshalfarc = Math.sqrt((1.0 - cos_2theta) / 2.0);
1856         boolean ccw = (ty0 - cy) * (tx1 - cx) > (ty1 - cy) * (tx0 - cx);
1857         // If the arc covers more than 90 degrees then we must use 2
1858         // cubic beziers to get a decent approximation.
1859         // arc = 180-tri
1860         // arc = 180-2*theta
1861         // arc > 90 implies 2*theta < 90
1862         // 2*theta < 90 implies cos_2theta > 0
1863         // So, we need 2 cubics if cos_2theta > 0
1864         if (cos_2theta <= 0.0) {
1865             // 1 cubic bezier
1866             double sinhalfarc = Math.sqrt((1.0 + cos_2theta) / 2.0);
1867             double cv = 4.0 / 3.0 * sinhalfarc / (1.0 + coshalfarc);
1868             if (ccw) cv = -cv;
1869             double cpx0 = tx0 - cv * (ty0 - cy);
1870             double cpy0 = ty0 + cv * (tx0 - cx);
1871             double cpx1 = tx1 + cv * (ty1 - cy);
1872             double cpy1 = ty1 - cv * (tx1 - cx);
1873             bezierCurveTo(cpx0, cpy0, cpx1, cpy1, tx1, ty1);
1874         } else {
1875             // 2 cubic beziers
1876             // We need sin(arc/4) and cos(arc/4)
1877             // We computed cos(arc/2), so we can compute them as follows:
1878             // sin(arc/4) = sqrt((1 - cos(arc/2)) / 2)
1879             // cos(arc/4) = sart((1 + cos(arc/2)) / 2)
1880             double sinqtrarc = Math.sqrt((1.0 - coshalfarc) / 2.0);
1881             double cosqtrarc = Math.sqrt((1.0 + coshalfarc) / 2.0);
1882             double cv = 4.0 / 3.0 * sinqtrarc / (1.0 + cosqtrarc);
1883             if (ccw) cv = -cv;
1884             double midratio = radius / Math.sqrt(lenratioden);
1885             double midarcx = cx + (x1 - mx) * midratio;
1886             double midarcy = cy + (y1 - my) * midratio;
1887             double cpx0 = tx0 - cv * (ty0 - cy);
1888             double cpy0 = ty0 + cv * (tx0 - cx);
1889             double cpx1 = midarcx + cv * (midarcy - cy);
1890             double cpy1 = midarcy - cv * (midarcx - cx);
1891             bezierCurveTo(cpx0, cpy0, cpx1, cpy1, midarcx, midarcy);
1892             cpx0 = midarcx - cv * (midarcy - cy);
1893             cpy0 = midarcy + cv * (midarcx - cx);
1894             cpx1 = tx1 + cv * (ty1 - cy);
1895             cpy1 = ty1 - cv * (tx1 - cx);
1896             bezierCurveTo(cpx0, cpy0, cpx1, cpy1, tx1, ty1);
1897         }
1898         return true;
1899     }
1900 
1901     /**
1902      * Adds path elements to the current path to make an arc that uses Euclidean
1903      * degrees. This Euclidean orientation sweeps from East to North, then West,
1904      * then South, then back to East.
1905      * The coordinates are transformed by the current transform as they are
1906      * added to the path and unaffected by subsequent changes to the transform.
1907      * The current path is a <a href="#path-attr">path attribute</a>
1908      * used for any of the path methods as specified in the
1909      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1910      * and <b>is not affected</b> by the {@link #save()} and
1911      * {@link #restore()} operations.
1912      *
1913      * @param centerX the center x position of the arc.
1914      * @param centerY the center y position of the arc.
1915      * @param radiusX the x radius of the arc.
1916      * @param radiusY the y radius of the arc.
1917      * @param startAngle the starting angle of the arc in the range {@code 0-360.0}
1918      * @param length  the length of the baseline of the arc.
1919      */
1920     public void arc(double centerX, double centerY,
1921                     double radiusX, double radiusY,
1922                     double startAngle, double length)
1923     {
1924         Arc2D arc = new Arc2D((float) (centerX - radiusX), // x
1925                               (float) (centerY - radiusY), // y
1926                               (float) (radiusX * 2.0), // w
1927                               (float) (radiusY * 2.0), // h
1928                               (float) startAngle,
1929                               (float) length,
1930                               Arc2D.OPEN);
1931         path.append(arc.getPathIterator(curState.transform), true);
1932         markPathDirty();
1933     }
1934 
1935     /**
1936      * Adds path elements to the current path to make a rectangle.
1937      * The coordinates are transformed by the current transform as they are
1938      * added to the path and unaffected by subsequent changes to the transform.
1939      * The current path is a <a href="#path-attr">path attribute</a>
1940      * used for any of the path methods as specified in the
1941      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1942      * and <b>is not affected</b> by the {@link #save()} and
1943      * {@link #restore()} operations.
1944      * 
1945      * @param x x position of the upper left corner of the rectangle.
1946      * @param y y position of the upper left corner of the rectangle.
1947      * @param w width of the rectangle.
1948      * @param h height of the rectangle.
1949      */
1950     public void rect(double x, double y, double w, double h) {
1951         coords[0] = (float) x;
1952         coords[1] = (float) y;
1953         coords[2] = (float) w;
1954         coords[3] = (float) 0;
1955         coords[4] = (float) 0;
1956         coords[5] = (float) h;
1957         curState.transform.deltaTransform(coords, 0, coords, 0, 3);
1958         float x0 = coords[0] + (float) curState.transform.getMxt();
1959         float y0 = coords[1] + (float) curState.transform.getMyt();
1960         float dx1 = coords[2];
1961         float dy1 = coords[3];
1962         float dx2 = coords[4];
1963         float dy2 = coords[5];
1964         path.moveTo(x0, y0);
1965         path.lineTo(x0+dx1, y0+dy1);
1966         path.lineTo(x0+dx1+dx2, y0+dy1+dy2);
1967         path.lineTo(x0+dx2, y0+dy2);
1968         path.closePath();
1969         markPathDirty();
1970 //        path.moveTo(x0, y0); // not needed, closepath leaves pen at moveto
1971     }
1972 
1973     /**
1974      * Appends an SVG Path string to the current path. If there is no current 
1975      * path the string must then start with either type of move command.
1976      * A {@code null} value or incorrect SVG path will be ignored.
1977      * The coordinates are transformed by the current transform as they are
1978      * added to the path and unaffected by subsequent changes to the transform.
1979      * The current path is a <a href="#path-attr">path attribute</a>
1980      * used for any of the path methods as specified in the
1981      * <a href="#attr-ops-table">Rendering Attributes Table</a>
1982      * and <b>is not affected</b> by the {@link #save()} and
1983      * {@link #restore()} operations.
1984      * 
1985      * @param svgpath the SVG Path string.
1986      */
1987     public void appendSVGPath(String svgpath) {
1988         if (svgpath == null) return;
1989         boolean prependMoveto = true;
1990         boolean skipMoveto = true;
1991         for (int i = 0; i < svgpath.length(); i++) {
1992             switch (svgpath.charAt(i)) {
1993                 case ' ':
1994                 case '\t':
1995                 case '\r':
1996                 case '\n':
1997                     continue;
1998                 case 'M':
1999                     prependMoveto = skipMoveto = false;
2000                     break;
2001                 case 'm':
2002                     if (path.getNumCommands() == 0) {
2003                         // An initial relative moveTo becomes absolute
2004                         prependMoveto = false;
2005                     }
2006                     // Even if we prepend an initial moveTo in the temp
2007                     // path, we do not want to delete the resulting initial
2008                     // moveTo because the relative moveto will be folded
2009                     // into it by an optimization in the Path2D object.
2010                     skipMoveto = false;
2011                     break;
2012             }
2013             break;
2014         }
2015         Path2D p2d = new Path2D();
2016         if (prependMoveto && path.getNumCommands() > 0) {
2017             float x0, y0;
2018             if (curState.transform.isTranslateOrIdentity()) {
2019                 x0 = (float) (path.getCurrentX() - curState.transform.getMxt());
2020                 y0 = (float) (path.getCurrentY() - curState.transform.getMyt());
2021             } else {
2022                 coords[0] = path.getCurrentX();
2023                 coords[1] = path.getCurrentY();
2024                 try {
2025                     curState.transform.inverseTransform(coords, 0, coords, 0, 1);
2026                 } catch (NoninvertibleTransformException e) {
2027                 }
2028                 x0 = coords[0];
2029                 y0 = coords[1];
2030             }
2031             p2d.moveTo(x0, y0);
2032         } else {
2033             skipMoveto = false;
2034         }
2035         try {
2036             p2d.appendSVGPath(svgpath);
2037             PathIterator pi = p2d.getPathIterator(curState.transform);
2038             if (skipMoveto) {
2039                 // We need to delete the initial moveto and let the path
2040                 // extend from the actual existing geometry.
2041                 pi.next();
2042             }
2043             path.append(pi, false);
2044         } catch (IllegalArgumentException | IllegalPathStateException ex) {
2045             //Ignore incorrect path
2046         }
2047     }
2048 
2049     /**
2050      * Closes the path.
2051      * The current path is a <a href="#path-attr">path attribute</a>
2052      * used for any of the path methods as specified in the
2053      * <a href="#attr-ops-table">Rendering Attributes Table</a>
2054      * and <b>is not affected</b> by the {@link #save()} and
2055      * {@link #restore()} operations.
2056      */
2057     public void closePath() {
2058         if (path.getNumCommands() > 0) {
2059             path.closePath();
2060             markPathDirty();
2061         }
2062     }
2063 
2064     /**
2065      * Fills the path with the current fill paint.
2066      * <p>
2067      * This method will be affected by any of the
2068      * <a href="#comm-attr">global common</a>,
2069      * <a href="#fill-attr">fill</a>,
2070      * or <a href="#path-attr">path</a>
2071      * attributes as specified in the
2072      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2073      * Note that the path segments were transformed as they were originally
2074      * added to the current path so the current transform will not affect
2075      * those path segments again, but it may affect other attributes in
2076      * affect at the time of the {@code fill()} operation.
2077      * </p>
2078      */
2079     public void fill() {
2080         writePath(NGCanvas.FILL_PATH);
2081     }
2082 
2083     /**
2084      * Strokes the path with the current stroke paint.
2085      * <p>
2086      * This method will be affected by any of the
2087      * <a href="#comm-attr">global common</a>,
2088      * <a href="#strk-attr">stroke</a>,
2089      * or <a href="#path-attr">path</a>
2090      * attributes as specified in the
2091      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2092      * Note that the path segments were transformed as they were originally
2093      * added to the current path so the current transform will not affect
2094      * those path segments again, but it may affect other attributes in
2095      * affect at the time of the {@code stroke()} operation.
2096      * </p>
2097      */
2098     public void stroke() {
2099         writePath(NGCanvas.STROKE_PATH);
2100     }
2101 
2102     /**
2103      * Intersects the current clip with the current path and applies it to
2104      * subsequent rendering operation as an anti-aliased mask.
2105      * The current clip is a <a href="#comm-attr">common attribute</a>
2106      * used for nearly all rendering operations as specified in the
2107      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2108      * <p>
2109      * This method will itself be affected only by the
2110      * <a href="#path-attr">path</a>
2111      * attributes as specified in the
2112      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2113      * Note that the path segments were transformed as they were originally
2114      * added to the current path so the current transform will not affect
2115      * those path segments again, but it may affect other attributes in
2116      * affect at the time of the {@code stroke()} operation.
2117      * </p>
2118      */
2119     public void clip() {
2120         Path2D clip = new Path2D(path);
2121         clipStack.addLast(clip);
2122         curState.numClipPaths++;
2123         GrowableDataBuffer buf = getBuffer();
2124         buf.putByte(NGCanvas.PUSH_CLIP);
2125         buf.putObject(clip);
2126     }
2127 
2128     /**
2129      * Returns true if the the given x,y point is inside the path.
2130      * 
2131      * @param x the X coordinate to use for the check.
2132      * @param y the Y coordinate to use for the check.
2133      * @return true if the point given is inside the path, false 
2134      * otherwise.
2135      */
2136     public boolean isPointInPath(double x, double y) {
2137         // TODO: HTML5 considers points on the path to be inside, but we
2138         // implement a halfin-halfout approach...
2139         return path.contains((float) x, (float) y);
2140     }
2141 
2142     /**
2143      * Clears a portion of the canvas with a transparent color value.
2144      * <p>
2145      * This method will be affected only by the current transform, clip,
2146      * and effect.
2147      * </p>
2148      * 
2149      * @param x X position of the upper left corner of the rectangle.
2150      * @param y Y position of the upper left corner of the rectangle.
2151      * @param w width of the rectangle.
2152      * @param h height of the rectangle.
2153      */
2154     public void clearRect(double x, double y, double w, double h) {
2155         if (w != 0 && h != 0) {
2156             resetIfCovers(null, x, y, w, h);
2157             writeOp4(x, y, w, h, NGCanvas.CLEAR_RECT);
2158         }
2159     }
2160 
2161     /**
2162      * Fills a rectangle using the current fill paint.
2163      * <p>
2164      * This method will be affected by any of the
2165      * <a href="#comm-attr">global common</a>
2166      * or <a href="#fill-attr">fill</a>
2167      * attributes as specified in the
2168      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2169      * </p>
2170      * 
2171      * @param x the X position of the upper left corner of the rectangle.
2172      * @param y the Y position of the upper left corner of the rectangle.
2173      * @param w the width of the rectangle.
2174      * @param h the height of the rectangle.
2175      */
2176     public void fillRect(double x, double y, double w, double h) {
2177         if (w != 0 && h != 0) {
2178             resetIfCovers(this.curState.fill, x, y, w, h);
2179             writeOp4(x, y, w, h, NGCanvas.FILL_RECT);
2180         }
2181     }
2182 
2183     /**
2184      * Strokes a rectangle using the current stroke paint.
2185      * <p>
2186      * This method will be affected by any of the
2187      * <a href="#comm-attr">global common</a>
2188      * or <a href="#strk-attr">stroke</a>
2189      * attributes as specified in the
2190      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2191      * </p>
2192      * 
2193      * @param x the X position of the upper left corner of the rectangle.
2194      * @param y the Y position of the upper left corner of the rectangle.
2195      * @param w the width of the rectangle.
2196      * @param h the height of the rectangle.
2197      */
2198     public void strokeRect(double x, double y, double w, double h) {
2199         if (w != 0 || h != 0) {
2200             writeOp4(x, y, w, h, NGCanvas.STROKE_RECT);
2201         }
2202     }
2203 
2204     /**
2205      * Fills an oval using the current fill paint.
2206      * <p>
2207      * This method will be affected by any of the
2208      * <a href="#comm-attr">global common</a>
2209      * or <a href="#fill-attr">fill</a>
2210      * attributes as specified in the
2211      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2212      * </p>
2213      * 
2214      * @param x the X coordinate of the upper left bound of the oval.
2215      * @param y the Y coordinate of the upper left bound of the oval.
2216      * @param w the width at the center of the oval.
2217      * @param h the height at the center of the oval.
2218      */
2219     public void fillOval(double x, double y, double w, double h) {
2220         if (w != 0 && h != 0) {
2221             writeOp4(x, y, w, h, NGCanvas.FILL_OVAL);
2222         }
2223     }
2224 
2225     /**
2226      * Strokes an oval using the current stroke paint.
2227      * <p>
2228      * This method will be affected by any of the
2229      * <a href="#comm-attr">global common</a>
2230      * or <a href="#strk-attr">stroke</a>
2231      * attributes as specified in the
2232      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2233      * </p>
2234      * 
2235      * @param x the X coordinate of the upper left bound of the oval.
2236      * @param y the Y coordinate of the upper left bound of the oval.
2237      * @param w the width at the center of the oval.
2238      * @param h the height at the center of the oval.
2239      */
2240     public void strokeOval(double x, double y, double w, double h) {
2241         if (w != 0 || h != 0) {
2242             writeOp4(x, y, w, h, NGCanvas.STROKE_OVAL);
2243         }
2244     }
2245 
2246     /**
2247      * Fills an arc using the current fill paint. A {@code null} ArcType or 
2248      * non positive width or height will cause the render command to be ignored.
2249      * <p>
2250      * This method will be affected by any of the
2251      * <a href="#comm-attr">global common</a>
2252      * or <a href="#fill-attr">fill</a>
2253      * attributes as specified in the
2254      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2255      * </p>
2256      * 
2257      * @param x the X coordinate of the arc.
2258      * @param y the Y coordinate of the arc.
2259      * @param w the width of the arc.
2260      * @param h the height of the arc.
2261      * @param startAngle the starting angle of the arc in degrees.
2262      * @param arcExtent the angular extent of the arc in degrees.
2263      * @param closure closure type (Round, Chord, Open) or null.
2264      */
2265     public void fillArc(double x, double y, double w, double h,
2266                         double startAngle, double arcExtent, ArcType closure)
2267     {
2268         if (w != 0 && h != 0 && closure != null) {
2269             writeArcType(closure);
2270             writeOp6(x, y, w, h, startAngle, arcExtent, NGCanvas.FILL_ARC);
2271         }
2272     }
2273 
2274     /**
2275      * Strokes an Arc using the current stroke paint. A {@code null} ArcType or 
2276      * non positive width or height will cause the render command to be ignored.
2277      * <p>
2278      * This method will be affected by any of the
2279      * <a href="#comm-attr">global common</a>
2280      * or <a href="#strk-attr">stroke</a>
2281      * attributes as specified in the
2282      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2283      * </p>
2284      *
2285      * @param x the X coordinate of the arc.
2286      * @param y the Y coordinate of the arc.
2287      * @param w the width of the arc.
2288      * @param h the height of the arc.
2289      * @param startAngle the starting angle of the arc in degrees.
2290      * @param arcExtent arcExtent the angular extent of the arc in degrees.
2291      * @param closure closure type (Round, Chord, Open) or null
2292      */
2293     public void strokeArc(double x, double y, double w, double h,
2294                         double startAngle, double arcExtent, ArcType closure)
2295     {
2296         if (w != 0 && h != 0 && closure != null) {
2297             writeArcType(closure);
2298             writeOp6(x, y, w, h, startAngle, arcExtent, NGCanvas.STROKE_ARC);
2299         }
2300     }
2301 
2302     /**
2303      * Fills a rounded rectangle using the current fill paint.
2304      * <p>
2305      * This method will be affected by any of the
2306      * <a href="#comm-attr">global common</a>
2307      * or <a href="#fill-attr">fill</a>
2308      * attributes as specified in the
2309      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2310      * </p>
2311      * 
2312      * @param x the X coordinate of the upper left bound of the oval.
2313      * @param y the Y coordinate of the upper left bound of the oval.
2314      * @param w the width at the center of the oval.
2315      * @param h the height at the center of the oval.
2316      * @param arcWidth the arc width of the rectangle corners.
2317      * @param arcHeight the arc height of the rectangle corners. 
2318      */
2319     public void fillRoundRect(double x, double y, double w, double h,
2320                               double arcWidth, double arcHeight)
2321     {
2322         if (w != 0 && h != 0) {
2323             writeOp6(x, y, w, h, arcWidth, arcHeight, NGCanvas.FILL_ROUND_RECT);
2324         }
2325     }
2326 
2327     /**
2328      * Strokes a rounded rectangle using the current stroke paint.
2329      * <p>
2330      * This method will be affected by any of the
2331      * <a href="#comm-attr">global common</a>
2332      * or <a href="#strk-attr">stroke</a>
2333      * attributes as specified in the
2334      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2335      * </p>
2336      * 
2337      * @param x the X coordinate of the upper left bound of the oval.
2338      * @param y the Y coordinate of the upper left bound of the oval.
2339      * @param w the width at the center of the oval.
2340      * @param h the height at the center of the oval.
2341      * @param arcWidth the arc width of the rectangle corners.
2342      * @param arcHeight the arc height of the rectangle corners.
2343      */
2344     public void strokeRoundRect(double x, double y, double w, double h,
2345                               double arcWidth, double arcHeight)
2346     {
2347         if (w != 0 && h != 0) {
2348             writeOp6(x, y, w, h, arcWidth, arcHeight, NGCanvas.STROKE_ROUND_RECT);
2349         }
2350     }
2351 
2352     /**
2353      * Strokes a line using the current stroke paint.
2354      * <p>
2355      * This method will be affected by any of the
2356      * <a href="#comm-attr">global common</a>
2357      * or <a href="#strk-attr">stroke</a>
2358      * attributes as specified in the
2359      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2360      * </p>
2361      * 
2362      * @param x1 the X coordinate of the starting point of the line.
2363      * @param y1 the Y coordinate of the starting point of the line.
2364      * @param x2 the X coordinate of the ending point of the line.
2365      * @param y2 the Y coordinate of the ending point of the line.
2366      */
2367     public void strokeLine(double x1, double y1, double x2, double y2) {
2368         writeOp4(x1, y1, x2, y2, NGCanvas.STROKE_LINE);
2369     }
2370 
2371     /**
2372      * Fills a polygon with the given points using the currently set fill paint.
2373      * A {@code null} value for any of the arrays will be ignored and nothing will be drawn.
2374      * <p>
2375      * This method will be affected by any of the
2376      * <a href="#comm-attr">global common</a>,
2377      * <a href="#fill-attr">fill</a>,
2378      * or <a href="#path-attr">Fill Rule</a>
2379      * attributes as specified in the
2380      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2381      * </p>
2382      * 
2383      * @param xPoints array containing the x coordinates of the polygon's points or null.
2384      * @param yPoints array containing the y coordinates of the polygon's points or null.
2385      * @param nPoints the number of points that make the polygon.
2386      */
2387     public void fillPolygon(double xPoints[], double yPoints[], int nPoints) {
2388         if (nPoints >= 3) {
2389             writePoly(xPoints, yPoints, nPoints, true, NGCanvas.FILL_PATH);
2390         }
2391     }
2392 
2393     /**
2394      * Strokes a polygon with the given points using the currently set stroke paint.
2395      * A {@code null} value for any of the arrays will be ignored and nothing will be drawn.
2396      * <p>
2397      * This method will be affected by any of the
2398      * <a href="#comm-attr">global common</a>
2399      * or <a href="#strk-attr">stroke</a>
2400      * attributes as specified in the
2401      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2402      * </p>
2403      * 
2404      * @param xPoints array containing the x coordinates of the polygon's points or null.
2405      * @param yPoints array containing the y coordinates of the polygon's points or null.
2406      * @param nPoints the number of points that make the polygon.
2407      */
2408     public void strokePolygon(double xPoints[], double yPoints[], int nPoints) {
2409         if (nPoints >= 2) {
2410             writePoly(xPoints, yPoints, nPoints, true, NGCanvas.STROKE_PATH);
2411         }
2412     }
2413 
2414     /**
2415      * Strokes a polyline with the given points using the currently set stroke 
2416      * paint attribute.
2417      * A {@code null} value for any of the arrays will be ignored and nothing will be drawn.
2418      * <p>
2419      * This method will be affected by any of the
2420      * <a href="#comm-attr">global common</a>
2421      * or <a href="#strk-attr">stroke</a>
2422      * attributes as specified in the
2423      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2424      * </p>
2425      * 
2426      * @param xPoints array containing the x coordinates of the polyline's points or null.
2427      * @param yPoints array containing the y coordinates of the polyline's points or null.
2428      * @param nPoints the number of points that make the polyline.
2429      */
2430     public void strokePolyline(double xPoints[], double yPoints[], int nPoints) {
2431         if (nPoints >= 2) {
2432             writePoly(xPoints, yPoints, nPoints, false, NGCanvas.STROKE_PATH);
2433         }
2434     }
2435 
2436     /**
2437      * Draws an image at the given x, y position using the width
2438      * and height of the given image.
2439      * A {@code null} image value or an image still in progress will be ignored.
2440      * <p>
2441      * This method will be affected by any of the
2442      * <a href="#comm-attr">global common</a>
2443      * attributes as specified in the
2444      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2445      * </p>
2446      * 
2447      * @param img the image to be drawn or null.
2448      * @param x the X coordinate on the destination for the upper left of the image.
2449      * @param y the Y coordinate on the destination for the upper left of the image.
2450      */
2451     public void drawImage(Image img, double x, double y) {
2452         if (img == null) return;
2453         double sw = img.getWidth();
2454         double sh = img.getHeight();
2455         writeImage(img, x, y, sw, sh);
2456     }
2457 
2458     /**
2459      * Draws an image into the given destination rectangle of the canvas. The
2460      * Image is scaled to fit into the destination rectagnle.
2461      * A {@code null} image value or an image still in progress will be ignored.
2462      * <p>
2463      * This method will be affected by any of the
2464      * <a href="#comm-attr">global common</a>
2465      * attributes as specified in the
2466      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2467      * </p>
2468      * 
2469      * @param img the image to be drawn or null.
2470      * @param x the X coordinate on the destination for the upper left of the image.
2471      * @param y the Y coordinate on the destination for the upper left of the image.
2472      * @param w the width of the destination rectangle. 
2473      * @param h the height of the destination rectangle.
2474      */
2475     public void drawImage(Image img, double x, double y, double w, double h) {
2476         writeImage(img, x, y, w, h);
2477     }
2478 
2479     /**
2480      * Draws the specified source rectangle of the given image to the given 
2481      * destination rectangle of the Canvas.
2482      * A {@code null} image value or an image still in progress will be ignored.
2483      * <p>
2484      * This method will be affected by any of the
2485      * <a href="#comm-attr">global common</a>
2486      * attributes as specified in the
2487      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2488      * </p>
2489      * 
2490      * @param img the image to be drawn or null.
2491      * @param sx the source rectangle's X coordinate position.
2492      * @param sy the source rectangle's Y coordinate position.
2493      * @param sw the source rectangle's width.
2494      * @param sh the source rectangle's height.
2495      * @param dx the destination rectangle's X coordinate position.
2496      * @param dy the destination rectangle's Y coordinate position.
2497      * @param dw the destination rectangle's width.
2498      * @param dh the destination rectangle's height.
2499      */
2500     public void drawImage(Image img,
2501                           double sx, double sy, double sw, double sh,
2502                           double dx, double dy, double dw, double dh)
2503     {
2504         writeImage(img, dx, dy, dw, dh, sx, sy, sw, sh);
2505     }
2506 
2507     private PixelWriter writer;
2508     /**
2509      * Returns a {@link PixelWriter} object that can be used to modify
2510      * the pixels of the {@link Canvas} associated with this
2511      * {@code GraphicsContext}.
2512      * All coordinates in the {@code PixelWriter} methods on the returned
2513      * object will be in device space since they refer directly to pixels
2514      * and no other rendering attributes will be applied when modifying
2515      * pixels using this object.
2516      * 
2517      * @return the {@code PixelWriter} for modifying the pixels of this
2518      *         {@code Canvas}
2519      */
2520     public PixelWriter getPixelWriter() {
2521         if (writer == null) {
2522             writer = new PixelWriter() {
2523                 @Override
2524                 public PixelFormat getPixelFormat() {
2525                     return PixelFormat.getByteBgraPreInstance();
2526                 }
2527 
2528                 private BytePixelSetter getSetter() {
2529                     return ByteBgraPre.setter;
2530                 }
2531 
2532                 @Override
2533                 public void setArgb(int x, int y, int argb) {
2534                     GrowableDataBuffer buf = getBuffer();
2535                     buf.putByte(NGCanvas.PUT_ARGB);
2536                     buf.putInt(x);
2537                     buf.putInt(y);
2538                     buf.putInt(argb);
2539                 }
2540 
2541                 @Override
2542                 public void setColor(int x, int y, Color c) {
2543                     if (c == null) throw new NullPointerException("Color cannot be null");
2544                     int a = (int) Math.round(c.getOpacity() * 255.0);
2545                     int r = (int) Math.round(c.getRed() * 255.0);
2546                     int g = (int) Math.round(c.getGreen() * 255.0);
2547                     int b = (int) Math.round(c.getBlue() * 255.0);
2548                     setArgb(x, y, (a << 24) | (r << 16) | (g << 8) | b);
2549                 }
2550 
2551                 private void writePixelBuffer(int x, int y, int w, int h,
2552                                               byte[] pixels)
2553                 {
2554                     GrowableDataBuffer buf = getBuffer();
2555                     buf.putByte(NGCanvas.PUT_ARGBPRE_BUF);
2556                     buf.putInt(x);
2557                     buf.putInt(y);
2558                     buf.putInt(w);
2559                     buf.putInt(h);
2560                     buf.putObject(pixels);
2561                 }
2562 
2563                 private int[] checkBounds(int x, int y, int w, int h,
2564                                           PixelFormat pf, int scan)
2565                 {
2566                     // assert (w >= 0 && h >= 0) - checked by caller
2567                     int cw = (int) Math.ceil(theCanvas.getWidth());
2568                     int ch = (int) Math.ceil(theCanvas.getHeight());
2569                     if (x >= 0 && y >= 0 && x+w <= cw && y+h <= ch) {
2570                         return null;
2571                     }
2572                     int offset = 0;
2573                     if (x < 0) {
2574                         w += x;
2575                         if (w < 0) return null;
2576                         if (pf != null) {
2577                             switch (pf.getType()) {
2578                                 case BYTE_BGRA:
2579                                 case BYTE_BGRA_PRE:
2580                                     offset -= x * 4;
2581                                     break;
2582                                 case BYTE_RGB:
2583                                     offset -= x * 3;
2584                                     break;
2585                                 case BYTE_INDEXED:
2586                                 case INT_ARGB:
2587                                 case INT_ARGB_PRE:
2588                                     offset -= x;
2589                                     break;
2590                                 default:
2591                                     throw new InternalError("unknown Pixel Format");
2592                             }
2593                         }
2594                         x = 0;
2595                     }
2596                     if (y < 0) {
2597                         h += y;
2598                         if (h < 0) return null;
2599                         offset -= y * scan;
2600                         y = 0;
2601                     }
2602                     if (x + w > cw) {
2603                         w = cw - x;
2604                         if (w < 0) return null;
2605                     }
2606                     if (y + h > ch) {
2607                         h = ch - y;
2608                         if (h < 0) return null;
2609                     }
2610                     return new int[] {
2611                         x, y, w, h, offset
2612                     };
2613                 }
2614 
2615                 @Override
2616                 public <T extends Buffer> void
2617                     setPixels(int x, int y, int w, int h,
2618                               PixelFormat<T> pixelformat,
2619                               T buffer, int scan)
2620                 {
2621                     if (pixelformat == null) throw new NullPointerException("PixelFormat cannot be null");
2622                     if (buffer == null) throw new NullPointerException("Buffer cannot be null");
2623                     if (w <= 0 || h <= 0) return;
2624                     int offset = buffer.position();
2625                     int adjustments[] = checkBounds(x, y, w, h,
2626                                                     pixelformat, scan);
2627                     if (adjustments != null) {
2628                         x = adjustments[0];
2629                         y = adjustments[1];
2630                         w = adjustments[2];
2631                         h = adjustments[3];
2632                         offset += adjustments[4];
2633                     }
2634 
2635                     byte pixels[] = new byte[w * h * 4];
2636                     ByteBuffer dst = ByteBuffer.wrap(pixels);
2637 
2638                     PixelGetter<T> getter = PixelUtils.getGetter(pixelformat);
2639                     PixelConverter<T, ByteBuffer> converter =
2640                         PixelUtils.getConverter(getter, getSetter());
2641                     converter.convert(buffer, offset, scan,
2642                                       dst, 0, w * 4,
2643                                       w, h);
2644                     writePixelBuffer(x, y, w, h, pixels);
2645                 }
2646 
2647                 @Override
2648                 public void setPixels(int x, int y, int w, int h,
2649                                       PixelFormat<ByteBuffer> pixelformat,
2650                                       byte[] buffer, int offset, int scanlineStride)
2651                 {
2652                     if (pixelformat == null) throw new NullPointerException("PixelFormat cannot be null");
2653                     if (buffer == null) throw new NullPointerException("Buffer cannot be null");
2654                     if (w <= 0 || h <= 0) return;
2655                     int adjustments[] = checkBounds(x, y, w, h,
2656                                                     pixelformat, scanlineStride);
2657                     if (adjustments != null) {
2658                         x = adjustments[0];
2659                         y = adjustments[1];
2660                         w = adjustments[2];
2661                         h = adjustments[3];
2662                         offset += adjustments[4];
2663                     }
2664 
2665                     byte pixels[] = new byte[w * h * 4];
2666 
2667                     BytePixelGetter getter = PixelUtils.getByteGetter(pixelformat);
2668                     ByteToBytePixelConverter converter =
2669                         PixelUtils.getB2BConverter(getter, getSetter());
2670                     converter.convert(buffer, offset, scanlineStride,
2671                                       pixels, 0, w * 4,
2672                                       w, h);
2673                     writePixelBuffer(x, y, w, h, pixels);
2674                 }
2675 
2676                 @Override
2677                 public void setPixels(int x, int y, int w, int h,
2678                                       PixelFormat<IntBuffer> pixelformat,
2679                                       int[] buffer, int offset, int scanlineStride)
2680                 {
2681                     if (pixelformat == null) throw new NullPointerException("PixelFormat cannot be null");
2682                     if (buffer == null) throw new NullPointerException("Buffer cannot be null");
2683                     if (w <= 0 || h <= 0) return;
2684                     int adjustments[] = checkBounds(x, y, w, h,
2685                                                     pixelformat, scanlineStride);
2686                     if (adjustments != null) {
2687                         x = adjustments[0];
2688                         y = adjustments[1];
2689                         w = adjustments[2];
2690                         h = adjustments[3];
2691                         offset += adjustments[4];
2692                     }
2693 
2694                     byte pixels[] = new byte[w * h * 4];
2695 
2696                     IntPixelGetter getter = PixelUtils.getIntGetter(pixelformat);
2697                     IntToBytePixelConverter converter =
2698                         PixelUtils.getI2BConverter(getter, getSetter());
2699                     converter.convert(buffer, offset, scanlineStride,
2700                                       pixels, 0, w * 4,
2701                                       w, h);
2702                     writePixelBuffer(x, y, w, h, pixels);
2703                 }
2704 
2705                 @Override
2706                 public void setPixels(int dstx, int dsty, int w, int h,
2707                                       PixelReader reader, int srcx, int srcy)
2708                 {
2709                     if (reader == null) throw new NullPointerException("Reader cannot be null");
2710                     if (w <= 0 || h <= 0) return;
2711                     int adjustments[] = checkBounds(dstx, dsty, w, h, null, 0);
2712                     if (adjustments != null) {
2713                         int newx = adjustments[0];
2714                         int newy = adjustments[1];
2715                         srcx += newx - dstx;
2716                         srcy += newy - dsty;
2717                         dstx = newx;
2718                         dsty = newy;
2719                         w = adjustments[2];
2720                         h = adjustments[3];
2721                     }
2722 
2723                     byte pixels[] = new byte[w * h * 4];
2724                     reader.getPixels(srcx, srcy, w, h,
2725                                      PixelFormat.getByteBgraPreInstance(),
2726                                      pixels, 0, w * 4);
2727                     writePixelBuffer(dstx, dsty, w, h, pixels);
2728                 }
2729             };
2730         }
2731         return writer;
2732     }
2733 
2734     /**
2735      * Sets the effect to be applied after the next draw call, or null to
2736      * disable effects.
2737      * The current effect is a <a href="#comm-attr">common attribute</a>
2738      * used for nearly all rendering operations as specified in the
2739      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2740      * 
2741      * @param e the effect to use, or null to disable effects
2742      */
2743     public void setEffect(Effect e) {
2744         GrowableDataBuffer buf = getBuffer();
2745         buf.putByte(NGCanvas.EFFECT);
2746         if (e == null) {
2747             curState.effect = null;
2748             buf.putObject(null);
2749         } else {
2750             curState.effect = e.impl_copy();
2751             curState.effect.impl_sync();
2752             buf.putObject(curState.effect.impl_getImpl());
2753         }
2754     }
2755     
2756     /**
2757      * Gets a copy of the effect to be applied after the next draw call.
2758      * A null return value means that no effect will be applied after subsequent
2759      * rendering calls.
2760      * The current effect is a <a href="#comm-attr">common attribute</a>
2761      * used for nearly all rendering operations as specified in the
2762      * <a href="#attr-ops-table">Rendering Attributes Table</a>.
2763      * 
2764      * @param e an {@code Effect} object that may be used to store the
2765      *        copy of the current effect, if it is of a compatible type
2766      * @return the current effect used for all rendering calls,
2767      *         or null if there is no current effect
2768      */
2769     public Effect getEffect(Effect e) {
2770         return curState.effect == null ? null : curState.effect.impl_copy();
2771     }
2772 
2773     /**
2774      * Applies the given effect to the entire bounds of the canvas and stores
2775      * the result back into the same canvas.
2776      * A {@code null} value will be ignored.
2777      * The effect will be applied without any other rendering attributes and
2778      * under an Identity coordinate transform.
2779      * Since the effect is applied to the entire bounds of the canvas, some
2780      * effects may have a confusing result, such as a Reflection effect
2781      * that will apply its reflection off of the bottom of the canvas even if
2782      * only a portion of the canvas has been rendered to and will not be
2783      * visible unless a negative offset is used to bring the reflection back
2784      * into view.
2785      * 
2786      * @param e the effect to apply onto the entire destination or null.
2787      */
2788     public void applyEffect(Effect e) {
2789         if (e == null) return;
2790         GrowableDataBuffer buf = getBuffer();
2791         buf.putByte(NGCanvas.FX_APPLY_EFFECT);
2792         Effect effect = e.impl_copy();
2793         effect.impl_sync();
2794         buf.putObject(effect.impl_getImpl());
2795     }
2796 }