changeset 10802:fd7b2574df75

Merge
author kcr
date Wed, 20 Dec 2017 12:22:56 -0800
parents a9865c1bacad 6e6bf169ee92
children e70664fd3af6
files build.gradle modules/javafx.web/src/main/native/Tools/DumpRenderTree/java/TestRunnerJava.cpp
diffstat 191 files changed, 4428 insertions(+), 1409 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri Dec 08 13:01:12 2017 -0800
+++ b/.hgtags	Wed Dec 20 12:22:56 2017 -0800
@@ -472,3 +472,5 @@
 c69cde9a039ebb766f35872231b8c97f2ff5f42d jdk-10+33
 3932162186a7b4c53945256818eb7723d1b6a71e jdk-10+34
 90773bfb2a58cce7c1aede30cc47f28cb9c53fe6 jdk-10+35
+674513271a88e51bcd2fb598634519d5f5f46270 jdk-10+36
+afed83541a56745884906842049658b84f1c2650 jdk-10+37
--- a/build.gradle	Fri Dec 08 13:01:12 2017 -0800
+++ b/build.gradle	Wed Dec 20 12:22:56 2017 -0800
@@ -31,8 +31,8 @@
  *      - some things worth automatically sanity checking:
  *          - are there images in the javadocs?
  *          - are all of the expected dylibs etc there?
- *  - Perform sanity checking to make sure a JDK exists with javac, javah, etc
- *  - Support building with no known JDK location, as long as javac, javah, etc are on the path
+ *  - Perform sanity checking to make sure a JDK exists with javac, etc
+ *  - Support building with no known JDK location, as long as javac, etc are on the path
  *  - Check all of the native flags. We're adding weight to some libs that don't need it, and so forth.
  *
  * Additional projects to work on as we go:
@@ -109,6 +109,25 @@
     return ret
 }
 
+/**
+ * Converts cygwin file paths for java executables to windows style
+ * executable paths by changing forward slashes to back slashes and
+ * adding the '.exe' extension.
+ * This method is safe to call from any platform, and will only do work if
+ * called on Windows (in all other cases it simply returns the supplied path).
+ *
+ * @param path the path to convert
+ * @return the path converted to windows style, if on windows, otherwise it
+ *         is the supplied path.
+ */
+String cygpathExe(String path) {
+    if (!IS_WINDOWS) return path;
+    if (path == null || "".equals(path)) return path;
+    String ret = path.replaceAll('/', '\\\\')
+    logger.info("Converting path '$path' via cygpath to "+ret)
+    return ret + ".exe"
+}
+
 void loadProperties(String sourceFileName) {
     def config = new Properties()
     def propFile = new File(sourceFileName)
@@ -311,10 +330,9 @@
         javaHome) // we have to bail and set it to something and this is as good as any!
 ext.JAVA_HOME = JDK_HOME
 
-defineProperty("JAVA", cygpath("$JDK_HOME/bin/java${IS_WINDOWS ? '.exe' : ''}"))
-defineProperty("JAVAC", cygpath("$JDK_HOME/bin/javac${IS_WINDOWS ? '.exe' : ''}"))
-defineProperty("JAVAH", cygpath("$JDK_HOME/bin/javah${IS_WINDOWS ? '.exe' : ''}"))
-defineProperty("JAVADOC", cygpath("$JDK_HOME/bin/javadoc${IS_WINDOWS ? '.exe' : ''}"))
+defineProperty("JAVA", cygpathExe("$JDK_HOME/bin/java"))
+defineProperty("JAVAC", cygpathExe("$JDK_HOME/bin/javac"))
+defineProperty("JAVADOC", cygpathExe("$JDK_HOME/bin/javadoc"))
 defineProperty("JDK_DOCS", "http://download.java.net/java/jdk9/docs/api/")
 defineProperty("JDK_JMODS", cygpath(System.getenv("JDK_JMODS")) ?: cygpath(System.getenv("JDK_HOME") + "/jmods"))
 
@@ -1031,7 +1049,6 @@
 // Make sure JDK_HOME/bin/java exists
 if (!file(JAVA).exists()) throw new Exception("Missing or incorrect path to 'java': '$JAVA'. Perhaps bad JDK_HOME? $JDK_HOME")
 if (!file(JAVAC).exists()) throw new Exception("Missing or incorrect path to 'javac': '$JAVAC'. Perhaps bad JDK_HOME? $JDK_HOME")
-if (!file(JAVAH).exists()) throw new Exception("Missing or incorrect path to 'javah': '$JAVAH'. Perhaps bad JDK_HOME? $JDK_HOME")
 if (!file(JAVADOC).exists()) throw new Exception("Missing or incorrect path to 'javadoc': '$JAVADOC'. Perhaps bad JDK_HOME? $JDK_HOME")
 
 // Determine the verion of Java in JDK_HOME. It looks like this:
@@ -1156,11 +1173,9 @@
  *                                                                            *
  *                Definition of Native Code Compilation Tasks                 *
  *                                                                            *
- *    - JavaHeaderTask is used to run javah. The JAVAH property will point at *
- *      the version of javah to be used (i.e.: a path to javah)               *
  *    - CCTask compiles native code. Specifically it will compile .m, .c,     *
- *      .cpp, or .cc files. It uses the headers provided by the               *
- *      JavaHeaderTask plus additional platform specific headers. It will     *
+ *      .cpp, or .cc files. It uses the headers provided by running           *
+ *      'javac -h' plus additional platform specific headers. It will         *
  *      compile into .obj files.                                              *
  *    - LinkTask will perform native linking and create the .dll / .so /      *
  *      .dylib as necessary.                                                  *
@@ -1173,8 +1188,8 @@
 ext.BUILD_SRC = rootProject.files("buildSrc/build/libs/buildSrc.jar")
 
 /**
- * Convenience method for creating javah, cc, link, and "native" tasks in the given project. These
- * tasks are parameterized by name, so that we can produce, for example, javahGlass, ccGlass, etc
+ * Convenience method for creating cc, link, and "native" tasks in the given project. These
+ * tasks are parameterized by name, so that we can produce, for example, ccGlass, etc
  * named tasks.
  *
  * @param project The project to add tasks to
@@ -1200,7 +1215,7 @@
     // of these root dirs, with the name of the dir being the name of the target
     def nativeRootDir = project.file("$project.buildDir/native/$name")
     def libRootDir = project.file("$project.buildDir/libs/$name")
-    // For each compile target, create a javah / cc / link triplet
+    // For each compile target, create a cc / link pair
     compileTargets { t ->
         def targetProperties = project.rootProject.ext[t.upper]
         def library = targetProperties.library
@@ -3298,7 +3313,7 @@
                         if (IS_64) {
                             cmakeArgs = "$cmakeArgs -DCMAKE_SYSTEM_PROCESSOR=x86_64"
                         } else {
-                            cmakeArgs = "$cmakeArgs -DCMAKE_SYSTEM_PROCESSOR=x86 -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32"
+                            cmakeArgs = "$cmakeArgs -DCMAKE_SYSTEM_PROCESSOR=i586 -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32"
                         }
                     } else if (t.name.startsWith("arm")) {
                         fail("ARM target is not supported as of now.")
--- a/buildSrc/ios.gradle	Fri Dec 08 13:01:12 2017 -0800
+++ b/buildSrc/ios.gradle	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/buildSrc/mac.gradle	Fri Dec 08 13:01:12 2017 -0800
+++ b/buildSrc/mac.gradle	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/gradle.properties.template	Fri Dec 08 13:01:12 2017 -0800
+++ b/gradle.properties.template	Wed Dec 20 12:22:56 2017 -0800
@@ -136,7 +136,7 @@
 #COMPILE_TARGETS = all
 
 # The JDK_HOME must either be specified or will be inferred based on the JDK / JRE used to
-# execute Gradle. From the JDK_HOME are derived the JAVA, JAVAC, JAVAH, JAVADOC, and STUB_RUNTIME
+# execute Gradle. From the JDK_HOME are derived the JAVA, JAVAC, JAVADOC, and STUB_RUNTIME
 # properties, although each of these may be specified individually. In addition, the LIBRARY_STUB
 # property is derived from STUB_RUNTIME. Normally the gradle build
 # will work based on whatever "java" is being used to run gradle. It does this by looking for
@@ -147,7 +147,6 @@
 #JDK_HOME = /path/to/the/jdk
 #JAVA = /path/to/the/jdk/bin/java
 #JAVAC = /path/to/the/jdk/bin/javac
-#JAVAH = /path/to/the/jdk/bin/javah
 #JAVADOC = /path/to/the/jdk/bin/javadoc
 #STUB_RUNTIME = /path/to/the/jdk/jre
 #LIBRARY_STUB = /path/to/the/jdk/jre/xxx (different location depending on platform)
--- a/modules/javafx.base/src/main/java/javafx/beans/property/BooleanProperty.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/BooleanProperty.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/main/java/javafx/beans/property/DoubleProperty.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/DoubleProperty.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/main/java/javafx/beans/property/FloatProperty.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/FloatProperty.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/main/java/javafx/beans/property/IntegerProperty.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/IntegerProperty.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/main/java/javafx/beans/property/LongProperty.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/LongProperty.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/main/java/javafx/beans/value/WeakChangeListener.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/main/java/javafx/beans/value/WeakChangeListener.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.base/src/test/java/test/com/sun/javafx/runtime/VersionInfoTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.base/src/test/java/test/com/sun/javafx/runtime/VersionInfoTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/FakeFocusTextField.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/FakeFocusTextField.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/CheckBox.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/CheckBox.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/CustomMenuItem.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/CustomMenuItem.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/PasswordField.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/PasswordField.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/RadioButton.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/RadioButton.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/ToggleButton.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/ToggleButton.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -219,6 +219,7 @@
         if (ownerNode == null) return;
 
         final Bounds ownerBounds = ownerNode.localToScreen(ownerNode.getLayoutBounds());
+        if (ownerBounds == null) return;
 
         // shifting vertically
         final double rootPrefHeight = root.prefHeight(-1);
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Wed Dec 20 12:22:56 2017 -0800
@@ -51,6 +51,7 @@
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.event.WeakEventHandler;
+import javafx.geometry.Bounds;
 import javafx.geometry.NodeOrientation;
 import javafx.geometry.Pos;
 import javafx.scene.AccessibleAttribute;
@@ -318,7 +319,8 @@
 
         // When we click else where in the scene - menu selection should be cleared.
         mouseEventHandler = t -> {
-            if (!container.localToScreen(container.getLayoutBounds()).contains(t.getScreenX(), t.getScreenY())) {
+            Bounds containerScreenBounds = container.localToScreen(container.getLayoutBounds());
+            if (containerScreenBounds == null || !containerScreenBounds.contains(t.getScreenX(), t.getScreenY())) {
                 unSelectMenus();
             }
         };
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuButtonSkin.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuButtonSkin.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/AndroidFontFinder.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/AndroidFontFinder.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/MacFontFinder.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/MacFontFinder.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/PrismFontFactory.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/PrismFontFactory.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/PrismFontStrike.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/PrismFontStrike.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWFactory.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWFactory.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWFontFile.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWFontFile.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/iio/ImageStorage.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/iio/ImageStorage.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/CacheFilter.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/CacheFilter.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/ImageLoader.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/ImageLoader.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedSceneDT.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedSceneDT.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedSceneDnD.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedSceneDnD.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PrismImageLoader2.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PrismImageLoader2.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Curve.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Curve.java	Wed Dec 20 12:22:56 2017 -0800
@@ -56,12 +56,16 @@
              float x3, float y3,
              float x4, float y4)
     {
-        ax = 3.0f * (x2 - x3) + x4 - x1;
-        ay = 3.0f * (y2 - y3) + y4 - y1;
-        bx = 3.0f * (x1 - 2.0f * x2 + x3);
-        by = 3.0f * (y1 - 2.0f * y2 + y3);
-        cx = 3.0f * (x2 - x1);
-        cy = 3.0f * (y2 - y1);
+        final float dx32 = 3.0f * (x3 - x2);
+        final float dy32 = 3.0f * (y3 - y2);
+        final float dx21 = 3.0f * (x2 - x1);
+        final float dy21 = 3.0f * (y2 - y1);
+        ax = (x4 - x1) - dx32;
+        ay = (y4 - y1) - dy32;
+        bx = (dx32 - dx21);
+        by = (dy32 - dy21);
+        cx = dx21;
+        cy = dy21;
         dx = x1;
         dy = y1;
         dax = 3.0f * ax; day = 3.0f * ay;
@@ -72,11 +76,13 @@
              float x2, float y2,
              float x3, float y3)
     {
+        final float dx21 = (x2 - x1);
+        final float dy21 = (y2 - y1);
         ax = 0.0f; ay = 0.0f;
-        bx = x1 - 2.0f * x2 + x3;
-        by = y1 - 2.0f * y2 + y3;
-        cx = 2.0f * (x2 - x1);
-        cy = 2.0f * (y2 - y1);
+        bx = (x3 - x2) - dx21;
+        by = (y3 - y2) - dy21;
+        cx = 2.0f * dx21;
+        cy = 2.0f * dy21;
         dx = x1;
         dy = y1;
         dax = 0.0f; day = 0.0f;
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DCurve.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DCurve.java	Wed Dec 20 12:22:56 2017 -0800
@@ -56,12 +56,16 @@
              double x3, double y3,
              double x4, double y4)
     {
-        ax = 3.0d * (x2 - x3) + x4 - x1;
-        ay = 3.0d * (y2 - y3) + y4 - y1;
-        bx = 3.0d * (x1 - 2.0d * x2 + x3);
-        by = 3.0d * (y1 - 2.0d * y2 + y3);
-        cx = 3.0d * (x2 - x1);
-        cy = 3.0d * (y2 - y1);
+        final double dx32 = 3.0d * (x3 - x2);
+        final double dy32 = 3.0d * (y3 - y2);
+        final double dx21 = 3.0d * (x2 - x1);
+        final double dy21 = 3.0d * (y2 - y1);
+        ax = (x4 - x1) - dx32;
+        ay = (y4 - y1) - dy32;
+        bx = (dx32 - dx21);
+        by = (dy32 - dy21);
+        cx = dx21;
+        cy = dy21;
         dx = x1;
         dy = y1;
         dax = 3.0d * ax; day = 3.0d * ay;
@@ -72,11 +76,13 @@
              double x2, double y2,
              double x3, double y3)
     {
+        final double dx21 = (x2 - x1);
+        final double dy21 = (y2 - y1);
         ax = 0.0d; ay = 0.0d;
-        bx = x1 - 2.0d * x2 + x3;
-        by = y1 - 2.0d * y2 + y3;
-        cx = 2.0d * (x2 - x1);
-        cy = 2.0d * (y2 - y1);
+        bx = (x3 - x2) - dx21;
+        by = (y3 - y2) - dy21;
+        cx = 2.0d * dx21;
+        cy = 2.0d * dy21;
         dx = x1;
         dy = y1;
         dax = 0.0d; day = 0.0d;
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DDasher.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DDasher.java	Wed Dec 20 12:22:56 2017 -0800
@@ -137,7 +137,7 @@
                     dashOn = !dashOn;
                 }
             }
-        } else if (phase > 0) {
+        } else if (phase > 0.0d) {
             if (cycles >= MAX_CYCLES) {
                 phase = 0.0d;
             } else {
@@ -157,12 +157,13 @@
 
         this.dash = dash;
         this.dashLen = dashLen;
-        this.startPhase = this.phase = phase;
+        this.phase = phase;
+        this.startPhase = phase;
         this.startDashOn = dashOn;
         this.startIdx = sidx;
         this.starting = true;
-        needsMoveTo = false;
-        firstSegidx = 0;
+        this.needsMoveTo = false;
+        this.firstSegidx = 0;
 
         this.recycleDashes = recycleDashes;
 
@@ -201,8 +202,8 @@
     }
 
     @Override
-    public void moveTo(double x0, double y0) {
-        if (firstSegidx > 0) {
+    public void moveTo(final double x0, final double y0) {
+        if (firstSegidx != 0) {
             out.moveTo(sx, sy);
             emitFirstSegments();
         }
@@ -210,8 +211,10 @@
         this.idx = startIdx;
         this.dashOn = this.startDashOn;
         this.phase = this.startPhase;
-        this.sx = this.x0 = x0;
-        this.sy = this.y0 = y0;
+        this.sx = x0;
+        this.sy = y0;
+        this.x0 = x0;
+        this.y0 = y0;
         this.starting = true;
     }
 
@@ -236,7 +239,7 @@
     private void emitFirstSegments() {
         final double[] fSegBuf = firstSegmentsBuffer;
 
-        for (int i = 0; i < firstSegidx; ) {
+        for (int i = 0, len = firstSegidx; i < len; ) {
             int type = (int)fSegBuf[i];
             emitSeg(fSegBuf, i + 1, type);
             i += (type - 1);
@@ -251,48 +254,59 @@
     private int firstSegidx;
 
     // precondition: pts must be in relative coordinates (relative to x0,y0)
-    private void goTo(double[] pts, int off, final int type) {
-        double x = pts[off + type - 4];
-        double y = pts[off + type - 3];
-        if (dashOn) {
+    private void goTo(final double[] pts, final int off, final int type,
+                      final boolean on)
+    {
+        final int index = off + type;
+        final double x = pts[index - 4];
+        final double y = pts[index - 3];
+
+        if (on) {
             if (starting) {
-                int len = type - 1; // - 2 + 1
-                int segIdx = firstSegidx;
-                double[] buf = firstSegmentsBuffer;
-                if (segIdx + len  > buf.length) {
-                    if (DO_STATS) {
-                        rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
-                            .add(segIdx + len);
-                    }
-                    firstSegmentsBuffer = buf
-                        = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
-                                                             segIdx + len);
-                }
-                buf[segIdx++] = type;
-                len--;
-                // small arraycopy (2, 4 or 6) but with offset:
-                System.arraycopy(pts, off, buf, segIdx, len);
-                segIdx += len;
-                firstSegidx = segIdx;
+                goTo_starting(pts, off, type);
             } else {
                 if (needsMoveTo) {
+                    needsMoveTo = false;
                     out.moveTo(x0, y0);
-                    needsMoveTo = false;
                 }
                 emitSeg(pts, off, type);
             }
         } else {
-            starting = false;
+            if (starting) {
+                // low probability test (hotspot)
+                starting = false;
+            }
             needsMoveTo = true;
         }
         this.x0 = x;
         this.y0 = y;
     }
 
+    private void goTo_starting(final double[] pts, final int off, final int type) {
+        int len = type - 1; // - 2 + 1
+        int segIdx = firstSegidx;
+        double[] buf = firstSegmentsBuffer;
+
+        if (segIdx + len  > buf.length) {
+            if (DO_STATS) {
+                rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
+                    .add(segIdx + len);
+            }
+            firstSegmentsBuffer = buf
+                = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
+                                                     segIdx + len);
+        }
+        buf[segIdx++] = type;
+        len--;
+        // small arraycopy (2, 4 or 6) but with offset:
+        System.arraycopy(pts, off, buf, segIdx, len);
+        firstSegidx = segIdx + len;
+    }
+
     @Override
-    public void lineTo(double x1, double y1) {
-        double dx = x1 - x0;
-        double dy = y1 - y0;
+    public void lineTo(final double x1, final double y1) {
+        final double dx = x1 - x0;
+        final double dy = y1 - y0;
 
         double len = dx*dx + dy*dy;
         if (len == 0.0d) {
@@ -307,48 +321,61 @@
 
         final double[] _curCurvepts = curCurvepts;
         final double[] _dash = dash;
+        final int _dashLen = this.dashLen;
+
+        int _idx = idx;
+        boolean _dashOn = dashOn;
+        double _phase = phase;
 
         double leftInThisDashSegment;
-        double dashdx, dashdy, p;
+        double d, dashdx, dashdy, p;
 
         while (true) {
-            leftInThisDashSegment = _dash[idx] - phase;
+            d = _dash[_idx];
+            leftInThisDashSegment = d - _phase;
 
             if (len <= leftInThisDashSegment) {
                 _curCurvepts[0] = x1;
                 _curCurvepts[1] = y1;
-                goTo(_curCurvepts, 0, 4);
+
+                goTo(_curCurvepts, 0, 4, _dashOn);
 
                 // Advance phase within current dash segment
-                phase += len;
+                _phase += len;
+
                 // TODO: compare double values using epsilon:
                 if (len == leftInThisDashSegment) {
-                    phase = 0.0d;
-                    idx = (idx + 1) % dashLen;
-                    dashOn = !dashOn;
+                    _phase = 0.0d;
+                    _idx = (_idx + 1) % _dashLen;
+                    _dashOn = !_dashOn;
                 }
+
+                // Save local state:
+                idx = _idx;
+                dashOn = _dashOn;
+                phase = _phase;
                 return;
             }
 
-            dashdx = _dash[idx] * cx;
-            dashdy = _dash[idx] * cy;
+            dashdx = d * cx;
+            dashdy = d * cy;
 
-            if (phase == 0.0d) {
+            if (_phase == 0.0d) {
                 _curCurvepts[0] = x0 + dashdx;
                 _curCurvepts[1] = y0 + dashdy;
             } else {
-                p = leftInThisDashSegment / _dash[idx];
+                p = leftInThisDashSegment / d;
                 _curCurvepts[0] = x0 + p * dashdx;
                 _curCurvepts[1] = y0 + p * dashdy;
             }
 
-            goTo(_curCurvepts, 0, 4);
+            goTo(_curCurvepts, 0, 4, _dashOn);
 
             len -= leftInThisDashSegment;
             // Advance to next dash segment
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
-            phase = 0.0d;
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
+            _phase = 0.0d;
         }
     }
 
@@ -357,43 +384,59 @@
 
     // preconditions: curCurvepts must be an array of length at least 2 * type,
     // that contains the curve we want to dash in the first type elements
-    private void somethingTo(int type) {
+    private void somethingTo(final int type) {
         if (pointCurve(curCurvepts, type)) {
             return;
         }
-        li.initializeIterationOnCurve(curCurvepts, type);
+        final LengthIterator _li = li;
+        final double[] _curCurvepts = curCurvepts;
+        final double[] _dash = dash;
+        final int _dashLen = this.dashLen;
+
+        _li.initializeIterationOnCurve(_curCurvepts, type);
+
+        int _idx = idx;
+        boolean _dashOn = dashOn;
+        double _phase = phase;
 
         // initially the current curve is at curCurvepts[0...type]
         int curCurveoff = 0;
         double lastSplitT = 0.0d;
         double t;
-        double leftInThisDashSegment = dash[idx] - phase;
+        double leftInThisDashSegment = _dash[_idx] - _phase;
 
-        while ((t = li.next(leftInThisDashSegment)) < 1.0d) {
+        while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
             if (t != 0.0d) {
                 DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT),
-                                    curCurvepts, curCurveoff,
-                                    curCurvepts, 0,
-                                    curCurvepts, type, type);
+                                    _curCurvepts, curCurveoff,
+                                    _curCurvepts, 0,
+                                    _curCurvepts, type, type);
                 lastSplitT = t;
-                goTo(curCurvepts, 2, type);
+                goTo(_curCurvepts, 2, type, _dashOn);
                 curCurveoff = type;
             }
             // Advance to next dash segment
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
-            phase = 0.0d;
-            leftInThisDashSegment = dash[idx];
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
+            _phase = 0.0d;
+            leftInThisDashSegment = _dash[_idx];
         }
-        goTo(curCurvepts, curCurveoff+2, type);
-        phase += li.lastSegLen();
-        if (phase >= dash[idx]) {
-            phase = 0.0d;
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
+
+        goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
+
+        _phase += _li.lastSegLen();
+        if (_phase >= _dash[_idx]) {
+            _phase = 0.0d;
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
         }
+        // Save local state:
+        idx = _idx;
+        dashOn = _dashOn;
+        phase = _phase;
+
         // reset LengthIterator:
-        li.reset();
+        _li.reset();
     }
 
     private static boolean pointCurve(double[] curve, int type) {
@@ -419,7 +462,7 @@
     // tree; however, the trees we are interested in have the property that
     // every non leaf node has exactly 2 children
     static final class LengthIterator {
-        private enum Side {LEFT, RIGHT};
+        private enum Side {LEFT, RIGHT}
         // Holds the curves at various levels of the recursion. The root
         // (i.e. the original curve) is at recCurveStack[0] (but then it
         // gets subdivided, the left half is put at 1, so most of the time
@@ -669,22 +712,23 @@
         // this is a bit of a hack. It returns -1 if we're not on a leaf, and
         // the length of the leaf if we are on a leaf.
         private double onLeaf() {
-            double[] curve = recCurveStack[recLevel];
+            final double[] curve = recCurveStack[recLevel];
+            final int _curveType = curveType;
             double polyLen = 0.0d;
 
             double x0 = curve[0], y0 = curve[1];
-            for (int i = 2; i < curveType; i += 2) {
+            for (int i = 2; i < _curveType; i += 2) {
                 final double x1 = curve[i], y1 = curve[i+1];
                 final double len = DHelpers.linelen(x0, y0, x1, y1);
                 polyLen += len;
-                curLeafCtrlPolyLengths[i/2 - 1] = len;
+                curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
                 x0 = x1;
                 y0 = y1;
             }
 
             final double lineLen = DHelpers.linelen(curve[0], curve[1],
-                                                  curve[curveType-2],
-                                                  curve[curveType-1]);
+                                                    curve[_curveType-2],
+                                                    curve[_curveType-1]);
             if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
                 return (polyLen + lineLen) / 2.0d;
             }
@@ -693,9 +737,9 @@
     }
 
     @Override
-    public void curveTo(double x1, double y1,
-                        double x2, double y2,
-                        double x3, double y3)
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double x3, final double y3)
     {
         final double[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = x0;        _curCurvepts[1] = y0;
@@ -706,7 +750,9 @@
     }
 
     @Override
-    public void quadTo(double x1, double y1, double x2, double y2) {
+    public void quadTo(final double x1, final double y1,
+                       final double x2, final double y2)
+    {
         final double[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = x0;        _curCurvepts[1] = y0;
         _curCurvepts[2] = x1;        _curCurvepts[3] = y1;
@@ -717,7 +763,7 @@
     @Override
     public void closePath() {
         lineTo(sx, sy);
-        if (firstSegidx > 0) {
+        if (firstSegidx != 0) {
             if (!dashOn || needsMoveTo) {
                 out.moveTo(sx, sy);
             }
@@ -728,7 +774,7 @@
 
     @Override
     public void pathDone() {
-        if (firstSegidx > 0) {
+        if (firstSegidx != 0) {
             out.moveTo(sx, sy);
             emitFirstSegments();
         }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DHelpers.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DHelpers.java	Wed Dec 20 12:22:56 2017 -0800
@@ -26,10 +26,9 @@
 package com.sun.marlin;
 
 import static java.lang.Math.PI;
-import static java.lang.Math.cos;
-import static java.lang.Math.sqrt;
-import static java.lang.Math.cbrt;
-import static java.lang.Math.acos;
+import java.util.Arrays;
+import com.sun.marlin.stats.Histogram;
+import com.sun.marlin.stats.StatLong;
 
 final class DHelpers implements MarlinConst {
 
@@ -115,17 +114,17 @@
         int num;
         if (D < 0.0d) {
             // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
-            final double phi = (1.0d/3.0d) * acos(-q / sqrt(-cb_p));
-            final double t = 2.0d * sqrt(-p);
+            final double phi = (1.0d/3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
+            final double t = 2.0d * Math.sqrt(-p);
 
-            pts[ off+0 ] = ( t * cos(phi));
-            pts[ off+1 ] = (-t * cos(phi + (PI / 3.0d)));
-            pts[ off+2 ] = (-t * cos(phi - (PI / 3.0d)));
+            pts[ off+0 ] = ( t * Math.cos(phi));
+            pts[ off+1 ] = (-t * Math.cos(phi + (PI / 3.0d)));
+            pts[ off+2 ] = (-t * Math.cos(phi - (PI / 3.0d)));
             num = 3;
         } else {
-            final double sqrt_D = sqrt(D);
-            final double u = cbrt(sqrt_D - q);
-            final double v = - cbrt(sqrt_D + q);
+            final double sqrt_D = Math.sqrt(D);
+            final double u =   Math.cbrt(sqrt_D - q);
+            final double v = - Math.cbrt(sqrt_D + q);
 
             pts[ off ] = (u + v);
             num = 1;
@@ -171,15 +170,6 @@
         return ret;
     }
 
-    static double polyLineLength(double[] poly, final int off, final int nCoords) {
-        assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
-        double acc = 0.0d;
-        for (int i = off + 2; i < off + nCoords; i += 2) {
-            acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
-        }
-        return acc;
-    }
-
     static double linelen(double x1, double y1, double x2, double y2) {
         final double dx = x2 - x1;
         final double dy = y2 - y1;
@@ -433,4 +423,388 @@
             return;
         }
     }
+
+    // From sun.java2d.loops.GeneralRenderer:
+
+    static int outcode(final double x, final double y,
+                       final double[] clipRect)
+    {
+        int code;
+        if (y < clipRect[0]) {
+            code = OUTCODE_TOP;
+        } else if (y >= clipRect[1]) {
+            code = OUTCODE_BOTTOM;
+        } else {
+            code = 0;
+        }
+        if (x < clipRect[2]) {
+            code |= OUTCODE_LEFT;
+        } else if (x >= clipRect[3]) {
+            code |= OUTCODE_RIGHT;
+        }
+        return code;
+    }
+
+    // a stack of polynomial curves where each curve shares endpoints with
+    // adjacent ones.
+    static final class PolyStack {
+        private static final byte TYPE_LINETO  = (byte) 0;
+        private static final byte TYPE_QUADTO  = (byte) 1;
+        private static final byte TYPE_CUBICTO = (byte) 2;
+
+        // curves capacity = edges count (8192) = edges x 2 (coords)
+        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
+
+        // types capacity = edges count (4096)
+        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
+
+        double[] curves;
+        int end;
+        byte[] curveTypes;
+        int numCurves;
+
+        // curves ref (dirty)
+        final DoubleArrayCache.Reference curves_ref;
+        // curveTypes ref (dirty)
+        final ByteArrayCache.Reference curveTypes_ref;
+
+        // used marks (stats only)
+        int curveTypesUseMark;
+        int curvesUseMark;
+
+        private final StatLong stat_polystack_types;
+        private final StatLong stat_polystack_curves;
+        private final Histogram hist_polystack_curves;
+        private final StatLong stat_array_polystack_curves;
+        private final StatLong stat_array_polystack_curveTypes;
+
+        PolyStack(final DRendererContext rdrCtx) {
+            this(rdrCtx, null, null, null, null, null);
+        }
+
+        PolyStack(final DRendererContext rdrCtx,
+                  final StatLong stat_polystack_types,
+                  final StatLong stat_polystack_curves,
+                  final Histogram hist_polystack_curves,
+                  final StatLong stat_array_polystack_curves,
+                  final StatLong stat_array_polystack_curveTypes)
+        {
+            curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
+            curves     = curves_ref.initial;
+
+            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
+            curveTypes     = curveTypes_ref.initial;
+            numCurves = 0;
+            end = 0;
+
+            if (DO_STATS) {
+                curveTypesUseMark = 0;
+                curvesUseMark = 0;
+            }
+            this.stat_polystack_types = stat_polystack_types;
+            this.stat_polystack_curves = stat_polystack_curves;
+            this.hist_polystack_curves = hist_polystack_curves;
+            this.stat_array_polystack_curves = stat_array_polystack_curves;
+            this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;
+        }
+
+        /**
+         * Disposes this PolyStack:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            end = 0;
+            numCurves = 0;
+
+            if (DO_STATS) {
+                stat_polystack_types.add(curveTypesUseMark);
+                stat_polystack_curves.add(curvesUseMark);
+                hist_polystack_curves.add(curvesUseMark);
+
+                // reset marks
+                curveTypesUseMark = 0;
+                curvesUseMark = 0;
+            }
+
+            // Return arrays:
+            // curves and curveTypes are kept dirty
+            curves     = curves_ref.putArray(curves);
+            curveTypes = curveTypes_ref.putArray(curveTypes);
+        }
+
+        private void ensureSpace(final int n) {
+            // use substraction to avoid integer overflow:
+            if (curves.length - end < n) {
+                if (DO_STATS) {
+                    stat_array_polystack_curves.add(end + n);
+                }
+                curves = curves_ref.widenArray(curves, end, end + n);
+            }
+            if (curveTypes.length <= numCurves) {
+                if (DO_STATS) {
+                    stat_array_polystack_curveTypes.add(numCurves + 1);
+                }
+                curveTypes = curveTypes_ref.widenArray(curveTypes,
+                                                       numCurves,
+                                                       numCurves + 1);
+            }
+        }
+
+        void pushCubic(double x0, double y0,
+                       double x1, double y1,
+                       double x2, double y2)
+        {
+            ensureSpace(6);
+            curveTypes[numCurves++] = TYPE_CUBICTO;
+            // we reverse the coordinate order to make popping easier
+            final double[] _curves = curves;
+            int e = end;
+            _curves[e++] = x2;    _curves[e++] = y2;
+            _curves[e++] = x1;    _curves[e++] = y1;
+            _curves[e++] = x0;    _curves[e++] = y0;
+            end = e;
+        }
+
+        void pushQuad(double x0, double y0,
+                      double x1, double y1)
+        {
+            ensureSpace(4);
+            curveTypes[numCurves++] = TYPE_QUADTO;
+            final double[] _curves = curves;
+            int e = end;
+            _curves[e++] = x1;    _curves[e++] = y1;
+            _curves[e++] = x0;    _curves[e++] = y0;
+            end = e;
+        }
+
+        void pushLine(double x, double y) {
+            ensureSpace(2);
+            curveTypes[numCurves++] = TYPE_LINETO;
+            curves[end++] = x;    curves[end++] = y;
+        }
+
+        void pullAll(final DPathConsumer2D io) {
+            final int nc = numCurves;
+            if (nc == 0) {
+                return;
+            }
+            if (DO_STATS) {
+                // update used marks:
+                if (numCurves > curveTypesUseMark) {
+                    curveTypesUseMark = numCurves;
+                }
+                if (end > curvesUseMark) {
+                    curvesUseMark = end;
+                }
+            }
+            final byte[]  _curveTypes = curveTypes;
+            final double[] _curves = curves;
+            int e = 0;
+
+            for (int i = 0; i < nc; i++) {
+                switch(_curveTypes[i]) {
+                case TYPE_LINETO:
+                    io.lineTo(_curves[e], _curves[e+1]);
+                    e += 2;
+                    continue;
+                case TYPE_QUADTO:
+                    io.quadTo(_curves[e+0], _curves[e+1],
+                              _curves[e+2], _curves[e+3]);
+                    e += 4;
+                    continue;
+                case TYPE_CUBICTO:
+                    io.curveTo(_curves[e+0], _curves[e+1],
+                               _curves[e+2], _curves[e+3],
+                               _curves[e+4], _curves[e+5]);
+                    e += 6;
+                    continue;
+                default:
+                }
+            }
+            numCurves = 0;
+            end = 0;
+        }
+
+        void popAll(final DPathConsumer2D io) {
+            int nc = numCurves;
+            if (nc == 0) {
+                return;
+            }
+            if (DO_STATS) {
+                // update used marks:
+                if (numCurves > curveTypesUseMark) {
+                    curveTypesUseMark = numCurves;
+                }
+                if (end > curvesUseMark) {
+                    curvesUseMark = end;
+                }
+            }
+            final byte[]  _curveTypes = curveTypes;
+            final double[] _curves = curves;
+            int e  = end;
+
+            while (nc != 0) {
+                switch(_curveTypes[--nc]) {
+                case TYPE_LINETO:
+                    e -= 2;
+                    io.lineTo(_curves[e], _curves[e+1]);
+                    continue;
+                case TYPE_QUADTO:
+                    e -= 4;
+                    io.quadTo(_curves[e+0], _curves[e+1],
+                              _curves[e+2], _curves[e+3]);
+                    continue;
+                case TYPE_CUBICTO:
+                    e -= 6;
+                    io.curveTo(_curves[e+0], _curves[e+1],
+                               _curves[e+2], _curves[e+3],
+                               _curves[e+4], _curves[e+5]);
+                    continue;
+                default:
+                }
+            }
+            numCurves = 0;
+            end = 0;
+        }
+
+        @Override
+        public String toString() {
+            String ret = "";
+            int nc = numCurves;
+            int last = end;
+            int len;
+            while (nc != 0) {
+                switch(curveTypes[--nc]) {
+                case TYPE_LINETO:
+                    len = 2;
+                    ret += "line: ";
+                    break;
+                case TYPE_QUADTO:
+                    len = 4;
+                    ret += "quad: ";
+                    break;
+                case TYPE_CUBICTO:
+                    len = 6;
+                    ret += "cubic: ";
+                    break;
+                default:
+                    len = 0;
+                }
+                last -= len;
+                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
+                                       + "\n";
+            }
+            return ret;
+        }
+    }
+
+    // a stack of integer indices
+    static final class IndexStack {
+
+        // integer capacity = edges count / 4 ~ 1024
+        private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2;
+
+        private int end;
+        private int[] indices;
+
+        // indices ref (dirty)
+        private final IntArrayCache.Reference indices_ref;
+
+        // used marks (stats only)
+        private int indicesUseMark;
+
+        private final StatLong stat_idxstack_indices;
+        private final Histogram hist_idxstack_indices;
+        private final StatLong stat_array_idxstack_indices;
+
+        IndexStack(final DRendererContext rdrCtx) {
+            this(rdrCtx, null, null, null);
+        }
+
+        IndexStack(final DRendererContext rdrCtx,
+                   final StatLong stat_idxstack_indices,
+                   final Histogram hist_idxstack_indices,
+                   final StatLong stat_array_idxstack_indices)
+        {
+            indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K
+            indices     = indices_ref.initial;
+            end = 0;
+
+            if (DO_STATS) {
+                indicesUseMark = 0;
+            }
+            this.stat_idxstack_indices = stat_idxstack_indices;
+            this.hist_idxstack_indices = hist_idxstack_indices;
+            this.stat_array_idxstack_indices = stat_array_idxstack_indices;
+        }
+
+        /**
+         * Disposes this PolyStack:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            end = 0;
+
+            if (DO_STATS) {
+                stat_idxstack_indices.add(indicesUseMark);
+                hist_idxstack_indices.add(indicesUseMark);
+
+                // reset marks
+                indicesUseMark = 0;
+            }
+
+            // Return arrays:
+            // values is kept dirty
+            indices = indices_ref.putArray(indices);
+        }
+
+        boolean isEmpty() {
+            return (end == 0);
+        }
+
+        void reset() {
+            end = 0;
+        }
+
+        void push(final int v) {
+            // remove redundant values (reverse order):
+            int[] _values = indices;
+            final int nc = end;
+            if (nc != 0) {
+                if (_values[nc - 1] == v) {
+                    // remove both duplicated values:
+                    end--;
+                    return;
+                }
+            }
+            if (_values.length <= nc) {
+                if (DO_STATS) {
+                    stat_array_idxstack_indices.add(nc + 1);
+                }
+                indices = _values = indices_ref.widenArray(_values, nc, nc + 1);
+            }
+            _values[end++] = v;
+
+            if (DO_STATS) {
+                // update used marks:
+                if (end > indicesUseMark) {
+                    indicesUseMark = end;
+                }
+            }
+        }
+
+        void pullAll(final double[] points, final DPathConsumer2D io) {
+            final int nc = end;
+            if (nc == 0) {
+                return;
+            }
+            final int[] _values = indices;
+
+            for (int i = 0, j; i < nc; i++) {
+                j = _values[i] << 1;
+                io.lineTo(points[j], points[j + 1]);
+            }
+            end = 0;
+        }
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DMarlinRenderer.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DMarlinRenderer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,9 +27,6 @@
 
 public interface DMarlinRenderer extends DPathConsumer2D {
 
-    public static final int WIND_EVEN_ODD = 0;
-    public static final int WIND_NON_ZERO = 1;
-
     public DMarlinRenderer init(final int pix_boundsX, final int pix_boundsY,
                                 final int pix_boundsWidth, final int pix_boundsHeight,
                                 final int windingRule);
@@ -45,4 +42,8 @@
     public int getOutpixMaxY();
 
     public void produceAlphas(MarlinAlphaConsumer ac);
+
+    public double getOffsetX();
+    public double getOffsetY();
+
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DMarlinRenderingEngine.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DMarlinRenderingEngine.java	Wed Dec 20 12:22:56 2017 -0800
@@ -46,14 +46,14 @@
     }
 
     static {
-        if (PathIterator.WIND_NON_ZERO != DMarlinRenderer.WIND_NON_ZERO ||
-            PathIterator.WIND_EVEN_ODD != DMarlinRenderer.WIND_EVEN_ODD ||
-            BasicStroke.JOIN_MITER != DStroker.JOIN_MITER ||
-            BasicStroke.JOIN_ROUND != DStroker.JOIN_ROUND ||
-            BasicStroke.JOIN_BEVEL != DStroker.JOIN_BEVEL ||
-            BasicStroke.CAP_BUTT != DStroker.CAP_BUTT ||
-            BasicStroke.CAP_ROUND != DStroker.CAP_ROUND ||
-            BasicStroke.CAP_SQUARE != DStroker.CAP_SQUARE)
+        if (PathIterator.WIND_NON_ZERO != WIND_NON_ZERO ||
+            PathIterator.WIND_EVEN_ODD != WIND_EVEN_ODD ||
+            BasicStroke.JOIN_MITER != JOIN_MITER ||
+            BasicStroke.JOIN_ROUND != JOIN_ROUND ||
+            BasicStroke.JOIN_BEVEL != JOIN_BEVEL ||
+            BasicStroke.CAP_BUTT != CAP_BUTT ||
+            BasicStroke.CAP_ROUND != CAP_ROUND ||
+            BasicStroke.CAP_SQUARE != CAP_SQUARE)
         {
             throw new InternalError("mismatched renderer constants");
         }
@@ -178,6 +178,10 @@
         // optimisation parameters
         logInfo("prism.marlin.useSimplifier    = "
                 + MarlinConst.USE_SIMPLIFIER);
+        logInfo("prism.marlin.clip             = "
+                + MarlinProperties.isDoClip());
+        logInfo("prism.marlin.clip.runtime.enable = "
+                + MarlinProperties.isDoClipRuntimeFlag());
 
         // debugging parameters
         logInfo("prism.marlin.doStats          = "
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DRenderer.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DRenderer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -43,6 +43,9 @@
     static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
     static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
 
+    private static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X;
+    private static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y;
+
     // common to all types of input path segments.
     // OFFSET as bytes
     // only integer values:
@@ -648,7 +651,7 @@
     }
 
     @Override
-    public void moveTo(double pix_x0, double pix_y0) {
+    public void moveTo(final double pix_x0, final double pix_y0) {
         closePath();
         final double sx = tosubpixx(pix_x0);
         final double sy = tosubpixy(pix_y0);
@@ -659,7 +662,7 @@
     }
 
     @Override
-    public void lineTo(double pix_x1, double pix_y1) {
+    public void lineTo(final double pix_x1, final double pix_y1) {
         final double x1 = tosubpixx(pix_x1);
         final double y1 = tosubpixy(pix_y1);
         addLine(x0, y0, x1, y1);
@@ -668,24 +671,26 @@
     }
 
     @Override
-    public void curveTo(double x1, double y1,
-            double x2, double y2,
-            double x3, double y3)
+    public void curveTo(final double pix_x1, final double pix_y1,
+                        final double pix_x2, final double pix_y2,
+                        final double pix_x3, final double pix_y3)
     {
-        final double xe = tosubpixx(x3);
-        final double ye = tosubpixy(y3);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
-                          tosubpixx(x2), tosubpixy(y2), xe, ye);
+        final double xe = tosubpixx(pix_x3);
+        final double ye = tosubpixy(pix_y3);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+                  tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
         curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
     }
 
     @Override
-    public void quadTo(double x1, double y1, double x2, double y2) {
-        final double xe = tosubpixx(x2);
-        final double ye = tosubpixy(y2);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+    public void quadTo(final double pix_x1, final double pix_y1,
+                       final double pix_x2, final double pix_y2)
+    {
+        final double xe = tosubpixx(pix_x2);
+        final double ye = tosubpixy(pix_y2);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
         quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
@@ -693,9 +698,11 @@
 
     @Override
     public void closePath() {
-        addLine(x0, y0, sx0, sy0);
-        x0 = sx0;
-        y0 = sy0;
+        if (x0 != sx0 || y0 != sy0) {
+            addLine(x0, y0, sx0, sy0);
+            x0 = sx0;
+            y0 = sy0;
+        }
     }
 
     @Override
@@ -1468,11 +1475,7 @@
             // ie number of primitives:
 
             // fast check min width:
-            if (width <= RLE_MIN_WIDTH) {
-                useRLE = false;
-            } else {
-                useRLE = true;
-            }
+            useRLE = (width > RLE_MIN_WIDTH);
         }
     }
 
@@ -1549,4 +1552,14 @@
     public int getOutpixMaxY() {
         return bboxY1;
     }
+
+    @Override
+    public double getOffsetX() {
+        return RDR_OFFSET_X;
+    }
+
+    @Override
+    public double getOffsetY() {
+        return RDR_OFFSET_Y;
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererContext.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererContext.java	Wed Dec 20 12:22:56 2017 -0800
@@ -72,6 +72,12 @@
     public final DDasher dasher;
     // flag indicating the shape is stroked (1) or filled (0)
     int stroking = 0;
+    // flag indicating to clip the shape
+    public boolean doClip = false;
+    // flag indicating if the path is closed or not (in advance) to handle properly caps
+    boolean closedPath = false;
+    // clip rectangle (ymin, ymax, xmin, xmax):
+    public final double[] clipRect = new double[4];
 
 // MarlinFX specific:
     // dirty bbox rectangle
@@ -80,14 +86,14 @@
     public MaskMarlinAlphaConsumer consumer = null;
 
     // Array caches:
-    /* clean int[] cache (zero-filled) = 4 refs */
-    private final IntArrayCache cleanIntCache = new IntArrayCache(true, 4);
-    /* dirty int[] cache = 4 refs */
-    private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 4);
-    /* dirty double[] cache = 3 refs */
-    private final DoubleArrayCache dirtyDoubleCache = new DoubleArrayCache(false, 3);
-    /* dirty byte[] cache = 1 ref */
-    private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 1);
+    /* clean int[] cache (zero-filled) = 5 refs */
+    private final IntArrayCache cleanIntCache = new IntArrayCache(true, 5);
+    /* dirty int[] cache = 5 refs */
+    private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 5);
+    /* dirty double[] cache = 4 refs (2 polystack) */
+    private final DoubleArrayCache dirtyDoubleCache = new DoubleArrayCache(false, 4);
+    /* dirty byte[] cache = 2 ref (2 polystack) */
+    private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 2);
 
     // RendererContext statistics
     final RendererStats stats;
@@ -115,7 +121,7 @@
         }
 
         // MarlinRenderingEngine.TransformingPathConsumer2D
-        transformerPC2D = new DTransformingPathConsumer2D();
+        transformerPC2D = new DTransformingPathConsumer2D(this);
 
         // Renderer shared memory:
         rdrMem = new DRendererSharedMemory(this);
@@ -138,7 +144,10 @@
             }
             stats.totalOffHeap = 0L;
         }
-        stroking = 0;
+        stroking   = 0;
+        doClip     = false;
+        closedPath = false;
+
         // if context is maked as DIRTY:
         if (dirty) {
             // may happen if an exception if thrown in the pipeline processing:
@@ -160,7 +169,7 @@
 
         // create a new Path2D ?
         if (p2d == null) {
-            p2d = new Path2D(Path2D.WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
+            p2d = new Path2D(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
 
             // update weak reference:
             refPath2D = new WeakReference<Path2D>(p2d);
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererNoAA.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererNoAA.java	Wed Dec 20 12:22:56 2017 -0800
@@ -37,6 +37,9 @@
 
     private static final double POWER_2_TO_32 = 0x1.0p32d;
 
+    private static final double RDR_OFFSET_X = 0.5d;
+    private static final double RDR_OFFSET_Y = 0.5d;
+
     // common to all types of input path segments.
     // OFFSET as bytes
     // only integer values:
@@ -640,7 +643,7 @@
     }
 
     @Override
-    public void moveTo(double pix_x0, double pix_y0) {
+    public void moveTo(final double pix_x0, final double pix_y0) {
         closePath();
         final double sx = tosubpixx(pix_x0);
         final double sy = tosubpixy(pix_y0);
@@ -651,7 +654,7 @@
     }
 
     @Override
-    public void lineTo(double pix_x1, double pix_y1) {
+    public void lineTo(final double pix_x1, final double pix_y1) {
         final double x1 = tosubpixx(pix_x1);
         final double y1 = tosubpixy(pix_y1);
         addLine(x0, y0, x1, y1);
@@ -660,24 +663,26 @@
     }
 
     @Override
-    public void curveTo(double x1, double y1,
-            double x2, double y2,
-            double x3, double y3)
+    public void curveTo(final double pix_x1, final double pix_y1,
+                        final double pix_x2, final double pix_y2,
+                        final double pix_x3, final double pix_y3)
     {
-        final double xe = tosubpixx(x3);
-        final double ye = tosubpixy(y3);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
-                          tosubpixx(x2), tosubpixy(y2), xe, ye);
+        final double xe = tosubpixx(pix_x3);
+        final double ye = tosubpixy(pix_y3);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+                  tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
         curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
     }
 
     @Override
-    public void quadTo(double x1, double y1, double x2, double y2) {
-        final double xe = tosubpixx(x2);
-        final double ye = tosubpixy(y2);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+    public void quadTo(final double pix_x1, final double pix_y1,
+                       final double pix_x2, final double pix_y2)
+    {
+        final double xe = tosubpixx(pix_x2);
+        final double ye = tosubpixy(pix_y2);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
         quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
@@ -685,9 +690,11 @@
 
     @Override
     public void closePath() {
-        addLine(x0, y0, sx0, sy0);
-        x0 = sx0;
-        y0 = sy0;
+        if (x0 != sx0 || y0 != sy0) {
+            addLine(x0, y0, sx0, sy0);
+            x0 = sx0;
+            y0 = sy0;
+        }
     }
 
     @Override
@@ -1399,11 +1406,7 @@
             // ie number of primitives:
 
             // fast check min width:
-            if (width <= RLE_MIN_WIDTH) {
-                useRLE = false;
-            } else {
-                useRLE = true;
-            }
+            useRLE = (width > RLE_MIN_WIDTH);
         }
     }
 
@@ -1480,4 +1483,14 @@
     public int getOutpixMaxY() {
         return bboxY1;
     }
+
+    @Override
+    public double getOffsetX() {
+        return RDR_OFFSET_X;
+    }
+
+    @Override
+    public double getOffsetY() {
+        return RDR_OFFSET_Y;
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DStroker.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DStroker.java	Wed Dec 20 12:22:56 2017 -0800
@@ -26,6 +26,7 @@
 package com.sun.marlin;
 
 import java.util.Arrays;
+import com.sun.marlin.DHelpers.PolyStack;
 
 // TODO: some of the arithmetic here is too verbose and prone to hard to
 // debug typos. We should consider making a small Point/Vector class that
@@ -36,42 +37,16 @@
     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
     private static final int CLOSE = 2;
 
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_MITER = 0;
-
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_ROUND = 1;
-
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_BEVEL = 2;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_BUTT = 0;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_ROUND = 1;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_SQUARE = 2;
-
     // pisces used to use fixed point arithmetic with 16 decimal digits. I
     // didn't want to change the values of the constant below when I converted
     // it to floating point, so that's why the divisions by 2^16 are there.
     private static final double ROUND_JOIN_THRESHOLD = 1000.0d/65536.0d;
 
-    private static final double C = 0.5522847498307933d;
+    // kappa = (4/3) * (SQRT(2) - 1)
+    private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+
+    // SQRT(2)
+    private static final double SQRT_2 = Math.sqrt(2.0d);
 
     private static final int MAX_N_CURVES = 11;
 
@@ -118,6 +93,20 @@
     // dirty curve
     final DCurve curve;
 
+    // Bounds of the drawing region, at pixel precision.
+    private double[] clipRect;
+
+    // the outcode of the current point
+    private int cOutCode = 0;
+
+    // the outcode of the starting point
+    private int sOutCode = 0;
+
+    // flag indicating if the path is opened (clipped)
+    private boolean opened = false;
+    // flag indicating if the starting point's cap is done
+    private boolean capStart = false;
+
     /**
      * Constructs a <code>DStroker</code>.
      * @param rdrCtx per-thread renderer context
@@ -125,7 +114,15 @@
     DStroker(final DRendererContext rdrCtx) {
         this.rdrCtx = rdrCtx;
 
-        this.reverse = new PolyStack(rdrCtx);
+        this.reverse = (rdrCtx.stats != null) ?
+            new PolyStack(rdrCtx,
+                    rdrCtx.stats.stat_str_polystack_types,
+                    rdrCtx.stats.stat_str_polystack_curves,
+                    rdrCtx.stats.hist_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_types)
+            : new PolyStack(rdrCtx);
+
         this.curve = rdrCtx.curve;
     }
 
@@ -141,13 +138,19 @@
      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
      * <code>JOIN_BEVEL</code>.
      * @param miterLimit the desired miter limit
+     * @param scale scaling factor applied to clip boundaries
+     * @param rdrOffX renderer's coordinate offset on X axis
+     * @param rdrOffY renderer's coordinate offset on Y axis
      * @return this instance
      */
-    public DStroker init(DPathConsumer2D pc2d,
-              double lineWidth,
-              int capStyle,
-              int joinStyle,
-              double miterLimit)
+    public DStroker init(final DPathConsumer2D pc2d,
+                         final double lineWidth,
+                         final int capStyle,
+                         final int joinStyle,
+                         final double miterLimit,
+                         final double scale,
+                         double rdrOffX,
+                         double rdrOffY)
     {
         this.out = pc2d;
 
@@ -156,13 +159,44 @@
         this.capStyle = capStyle;
         this.joinStyle = joinStyle;
 
-        double limit = miterLimit * lineWidth2;
+        final double limit = miterLimit * lineWidth2;
         this.miterLimitSq = limit * limit;
 
         this.prev = CLOSE;
 
         rdrCtx.stroking = 1;
 
+        if (rdrCtx.doClip) {
+            // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+            double margin = lineWidth2;
+
+            if (capStyle == CAP_SQUARE) {
+                margin *= SQRT_2;
+            }
+            if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+                margin = limit;
+            }
+            if (scale != 1.0d) {
+                margin  *= scale;
+                rdrOffX *= scale;
+                rdrOffY *= scale;
+            }
+            // add a small rounding error:
+            margin += 1e-3d;
+
+            // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+            // adjust clip rectangle (ymin, ymax, xmin, xmax):
+            final double[] _clipRect = rdrCtx.clipRect;
+            _clipRect[0] -= margin - rdrOffY;
+            _clipRect[1] += margin + rdrOffY;
+            _clipRect[2] -= margin - rdrOffX;
+            _clipRect[3] += margin + rdrOffX;
+            this.clipRect = _clipRect;
+        } else {
+            this.clipRect = null;
+            this.cOutCode = 0;
+            this.sOutCode = 0;
+        }
         return this; // fluent API
     }
 
@@ -173,6 +207,9 @@
     void dispose() {
         reverse.dispose();
 
+        opened   = false;
+        capStart = false;
+
         if (DO_CLEAN_DIRTY) {
             // Force zero-fill dirty arrays:
             Arrays.fill(offset0, 0.0d);
@@ -443,19 +480,62 @@
     }
 
     @Override
-    public void moveTo(double x0, double y0) {
-        if (prev == DRAWING_OP_TO) {
-            finish();
+    public void moveTo(final double x0, final double y0) {
+        moveTo(x0, y0, cOutCode);
+        // update starting point:
+        this.sx0 = x0;
+        this.sy0 = y0;
+        this.sdx = 1.0d;
+        this.sdy = 0.0d;
+        this.opened   = false;
+        this.capStart = false;
+
+        if (clipRect != null) {
+            final int outcode = DHelpers.outcode(x0, y0, clipRect);
+            this.cOutCode = outcode;
+            this.sOutCode = outcode;
         }
-        this.sx0 = this.cx0 = x0;
-        this.sy0 = this.cy0 = y0;
-        this.cdx = this.sdx = 1.0d;
-        this.cdy = this.sdy = 0.0d;
-        this.prev = MOVE_TO;
+    }
+
+    private void moveTo(final double x0, final double y0,
+                        final int outcode)
+    {
+        if (prev == MOVE_TO) {
+            this.cx0 = x0;
+            this.cy0 = y0;
+        } else {
+            if (prev == DRAWING_OP_TO) {
+                finish(outcode);
+            }
+            this.prev = MOVE_TO;
+            this.cx0 = x0;
+            this.cy0 = y0;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
+        }
     }
 
     @Override
-    public void lineTo(double x1, double y1) {
+    public void lineTo(final double x1, final double y1) {
+        lineTo(x1, y1, false);
+    }
+
+    private void lineTo(final double x1, final double y1,
+                        final boolean force)
+    {
+        final int outcode0 = this.cOutCode;
+        if (!force && clipRect != null) {
+            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+            this.cOutCode = outcode1;
+
+            // basic rejection criteria
+            if ((outcode0 & outcode1) != 0) {
+                moveTo(x1, y1, outcode0);
+                opened = true;
+                return;
+            }
+        }
+
         double dx = x1 - cx0;
         double dy = y1 - cy0;
         if (dx == 0.0d && dy == 0.0d) {
@@ -465,7 +545,7 @@
         final double mx = offset0[0];
         final double my = offset0[1];
 
-        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
+        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
 
         emitLineTo(cx0 + mx, cy0 + my);
         emitLineTo( x1 + mx,  y1 + my);
@@ -473,43 +553,65 @@
         emitLineToRev(cx0 - mx, cy0 - my);
         emitLineToRev( x1 - mx,  y1 - my);
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = x1;
+        this.cy0 = y1;
+        this.cdx = dx;
+        this.cdy = dy;
         this.cmx = mx;
         this.cmy = my;
-        this.cdx = dx;
-        this.cdy = dy;
-        this.cx0 = x1;
-        this.cy0 = y1;
-        this.prev = DRAWING_OP_TO;
     }
 
     @Override
     public void closePath() {
-        if (prev != DRAWING_OP_TO) {
+        // distinguish empty path at all vs opened path ?
+        if (prev != DRAWING_OP_TO && !opened) {
             if (prev == CLOSE) {
                 return;
             }
             emitMoveTo(cx0, cy0 - lineWidth2);
-            this.cmx = this.smx = 0.0d;
-            this.cmy = this.smy = -lineWidth2;
-            this.cdx = this.sdx = 1.0d;
-            this.cdy = this.sdy = 0.0d;
-            finish();
+
+            this.sdx = 1.0d;
+            this.sdy = 0.0d;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
+
+            this.smx = 0.0d;
+            this.smy = -lineWidth2;
+            this.cmx = 0.0d;
+            this.cmy = -lineWidth2;
+
+            finish(cOutCode);
             return;
         }
 
-        if (cx0 != sx0 || cy0 != sy0) {
-            lineTo(sx0, sy0);
+        // basic acceptance criteria
+        if ((sOutCode & cOutCode) == 0) {
+            if (cx0 != sx0 || cy0 != sy0) {
+                lineTo(sx0, sy0, true);
+            }
+
+            drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, sOutCode);
+
+            emitLineTo(sx0 + smx, sy0 + smy);
+
+            if (opened) {
+                emitLineTo(sx0 - smx, sy0 - smy);
+            } else {
+                emitMoveTo(sx0 - smx, sy0 - smy);
+            }
         }
-
-        drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
-
-        emitLineTo(sx0 + smx, sy0 + smy);
-
-        emitMoveTo(sx0 - smx, sy0 - smy);
+        // Ignore caps like finish(false)
         emitReverse();
 
         this.prev = CLOSE;
-        emitClose();
+
+        if (opened) {
+            // do not emit close
+            opened = false;
+        } else {
+            emitClose();
+        }
     }
 
     private void emitReverse() {
@@ -519,7 +621,7 @@
     @Override
     public void pathDone() {
         if (prev == DRAWING_OP_TO) {
-            finish();
+            finish(cOutCode);
         }
 
         out.pathDone();
@@ -532,23 +634,39 @@
         dispose();
     }
 
-    private void finish() {
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(cx0, cy0, cmx, cmy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
-            emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+    private void finish(final int outcode) {
+        // Problem: impossible to guess if the path will be closed in advance
+        //          i.e. if caps must be drawn or not ?
+        // Solution: use the ClosedPathDetector before Stroker to determine
+        // if the path is a closed path or not
+        if (!rdrCtx.closedPath) {
+            if (outcode == 0) {
+                // current point = end's cap:
+                if (capStyle == CAP_ROUND) {
+                    drawRoundCap(cx0, cy0, cmx, cmy);
+                } else if (capStyle == CAP_SQUARE) {
+                    emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
+                    emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+                }
+            }
+            emitReverse();
+
+            if (!capStart) {
+                capStart = true;
+
+                if (sOutCode == 0) {
+                    // starting point = initial cap:
+                    if (capStyle == CAP_ROUND) {
+                        drawRoundCap(sx0, sy0, -smx, -smy);
+                    } else if (capStyle == CAP_SQUARE) {
+                        emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
+                        emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+                    }
+                }
+            }
+        } else {
+            emitReverse();
         }
-
-        emitReverse();
-
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(sx0, sy0, -smx, -smy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
-            emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
-        }
-
         emitClose();
     }
 
@@ -620,23 +738,28 @@
                           double x0, double y0,
                           double dx, double dy,
                           double omx, double omy,
-                          double mx, double my)
+                          double mx, double my,
+                          final int outcode)
     {
         if (prev != DRAWING_OP_TO) {
             emitMoveTo(x0 + mx, y0 + my);
-            this.sdx = dx;
-            this.sdy = dy;
-            this.smx = mx;
-            this.smy = my;
+            if (!opened) {
+                this.sdx = dx;
+                this.sdy = dy;
+                this.smx = mx;
+                this.smy = my;
+            }
         } else {
-            boolean cw = isCW(pdx, pdy, dx, dy);
-            if (joinStyle == JOIN_MITER) {
-                drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
-            } else if (joinStyle == JOIN_ROUND) {
-                drawRoundJoin(x0, y0,
-                              omx, omy,
-                              mx, my, cw,
-                              ROUND_JOIN_THRESHOLD);
+            final boolean cw = isCW(pdx, pdy, dx, dy);
+            if (outcode == 0) {
+                if (joinStyle == JOIN_MITER) {
+                    drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
+                } else if (joinStyle == JOIN_ROUND) {
+                    drawRoundJoin(x0, y0,
+                                  omx, omy,
+                                  mx, my, cw,
+                                  ROUND_JOIN_THRESHOLD);
+                }
             }
             emitLineTo(x0, y0, !cw);
         }
@@ -941,10 +1064,29 @@
         return ret;
     }
 
-    @Override public void curveTo(double x1, double y1,
-                                  double x2, double y2,
-                                  double x3, double y3)
+    @Override
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double x3, final double y3)
     {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
+            this.cOutCode = outcode3;
+
+            if ((outcode0 & outcode3) != 0) {
+                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+                final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
+                    moveTo(x3, y3, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final double[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
@@ -953,7 +1095,7 @@
         mid[6] = x3;  mid[7] = y3;
 
         // need these so we can update the state at the end of this method
-        final double xf = mid[6], yf = mid[7];
+        final double xf = x3, yf = y3;
         double dxs = mid[2] - mid[0];
         double dys = mid[3] - mid[1];
         double dxf = mid[6] - mid[4];
@@ -979,6 +1121,10 @@
         }
         if (dxs == 0.0d && dys == 0.0d) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
@@ -997,7 +1143,7 @@
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
 
@@ -1032,16 +1178,36 @@
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = xf;
+        this.cy0 = yf;
+        this.cdx = dxf;
+        this.cdy = dyf;
         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cx0 = xf;
-        this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
     }
 
-    @Override public void quadTo(double x1, double y1, double x2, double y2) {
+    @Override
+    public void quadTo(final double x1, final double y1,
+                       final double x2, final double y2)
+    {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+            this.cOutCode = outcode2;
+
+            if ((outcode0 & outcode2) != 0) {
+                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2) != 0) {
+                    moveTo(x2, y2, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final double[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
@@ -1049,7 +1215,7 @@
         mid[4] = x2;  mid[5] = y2;
 
         // need these so we can update the state at the end of this method
-        final double xf = mid[4], yf = mid[5];
+        final double xf = x2, yf = y2;
         double dxs = mid[2] - mid[0];
         double dys = mid[3] - mid[1];
         double dxf = mid[4] - mid[2];
@@ -1060,6 +1226,10 @@
         }
         if (dxs == 0.0d && dys == 0.0d) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
@@ -1077,7 +1247,7 @@
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
 
@@ -1112,210 +1282,13 @@
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = xf;
+        this.cy0 = yf;
+        this.cdx = dxf;
+        this.cdy = dyf;
         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cx0 = xf;
-        this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
     }
 
-    // a stack of polynomial curves where each curve shares endpoints with
-    // adjacent ones.
-    static final class PolyStack {
-        private static final byte TYPE_LINETO  = (byte) 0;
-        private static final byte TYPE_QUADTO  = (byte) 1;
-        private static final byte TYPE_CUBICTO = (byte) 2;
-
-        // curves capacity = edges count (8192) = edges x 2 (coords)
-        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
-        // types capacity = edges count (4096)
-        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
-        double[] curves;
-        int end;
-        byte[] curveTypes;
-        int numCurves;
-
-        // per-thread renderer context
-        final DRendererContext rdrCtx;
-
-        // curves ref (dirty)
-        final DoubleArrayCache.Reference curves_ref;
-        // curveTypes ref (dirty)
-        final ByteArrayCache.Reference curveTypes_ref;
-
-        // used marks (stats only)
-        int curveTypesUseMark;
-        int curvesUseMark;
-
-        /**
-         * Constructor
-         * @param rdrCtx per-thread renderer context
-         */
-        PolyStack(final DRendererContext rdrCtx) {
-            this.rdrCtx = rdrCtx;
-
-            curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
-            curves     = curves_ref.initial;
-
-            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
-            curveTypes     = curveTypes_ref.initial;
-            numCurves = 0;
-            end = 0;
-
-            if (DO_STATS) {
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-        }
-
-        /**
-         * Disposes this PolyStack:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            end = 0;
-            numCurves = 0;
-
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);
-                rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark);
-                rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark);
-
-                // reset marks
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-
-            // Return arrays:
-            // curves and curveTypes are kept dirty
-            curves     = curves_ref.putArray(curves);
-            curveTypes = curveTypes_ref.putArray(curveTypes);
-        }
-
-        private void ensureSpace(final int n) {
-            // use substraction to avoid integer overflow:
-            if (curves.length - end < n) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curves
-                        .add(end + n);
-                }
-                curves = curves_ref.widenArray(curves, end, end + n);
-            }
-            if (curveTypes.length <= numCurves) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curveTypes
-                        .add(numCurves + 1);
-                }
-                curveTypes = curveTypes_ref.widenArray(curveTypes,
-                                                       numCurves,
-                                                       numCurves + 1);
-            }
-        }
-
-        void pushCubic(double x0, double y0,
-                       double x1, double y1,
-                       double x2, double y2)
-        {
-            ensureSpace(6);
-            curveTypes[numCurves++] = TYPE_CUBICTO;
-            // we reverse the coordinate order to make popping easier
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x2;    _curves[e++] = y2;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushQuad(double x0, double y0,
-                      double x1, double y1)
-        {
-            ensureSpace(4);
-            curveTypes[numCurves++] = TYPE_QUADTO;
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushLine(double x, double y) {
-            ensureSpace(2);
-            curveTypes[numCurves++] = TYPE_LINETO;
-            curves[end++] = x;    curves[end++] = y;
-        }
-
-        void popAll(DPathConsumer2D io) {
-            if (DO_STATS) {
-                // update used marks:
-                if (numCurves > curveTypesUseMark) {
-                    curveTypesUseMark = numCurves;
-                }
-                if (end > curvesUseMark) {
-                    curvesUseMark = end;
-                }
-            }
-            final byte[]  _curveTypes = curveTypes;
-            final double[] _curves = curves;
-            int nc = numCurves;
-            int e  = end;
-
-            while (nc != 0) {
-                switch(_curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    e -= 2;
-                    io.lineTo(_curves[e], _curves[e+1]);
-                    continue;
-                case TYPE_QUADTO:
-                    e -= 4;
-                    io.quadTo(_curves[e+0], _curves[e+1],
-                              _curves[e+2], _curves[e+3]);
-                    continue;
-                case TYPE_CUBICTO:
-                    e -= 6;
-                    io.curveTo(_curves[e+0], _curves[e+1],
-                               _curves[e+2], _curves[e+3],
-                               _curves[e+4], _curves[e+5]);
-                    continue;
-                default:
-                }
-            }
-            numCurves = 0;
-            end = 0;
-        }
-
-        @Override
-        public String toString() {
-            String ret = "";
-            int nc = numCurves;
-            int last = end;
-            int len;
-            while (nc != 0) {
-                switch(curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    len = 2;
-                    ret += "line: ";
-                    break;
-                case TYPE_QUADTO:
-                    len = 4;
-                    ret += "quad: ";
-                    break;
-                case TYPE_CUBICTO:
-                    len = 6;
-                    ret += "cubic: ";
-                    break;
-                default:
-                    len = 0;
-                }
-                last -= len;
-                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
-                                       + "\n";
-            }
-            return ret;
-        }
-    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/DTransformingPathConsumer2D.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/DTransformingPathConsumer2D.java	Wed Dec 20 12:22:56 2017 -0800
@@ -27,49 +27,175 @@
 
 import com.sun.javafx.geom.Path2D;
 import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.marlin.DHelpers.IndexStack;
+import com.sun.marlin.DHelpers.PolyStack;
 
 public final class DTransformingPathConsumer2D {
 
-    DTransformingPathConsumer2D() {
-        // used by DRendererContext
-    }
+    private final DRendererContext rdrCtx;
 
-    // recycled DPathConsumer2D instance from wrapPath2d()
+    // recycled ClosedPathDetector instance from detectClosedPath()
+    private final ClosedPathDetector   cpDetector;
+
+    // recycled PathClipFilter instance from pathClipper()
+    private final PathClipFilter       pathClipper;
+
+    // recycled DPathConsumer2D instance from wrapPath2D()
     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
 
     // recycled DPathConsumer2D instances from deltaTransformConsumer()
     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
 
+    // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
+    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
+    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+
+    // recycled PathTracer instances from tracer...() methods
+    private final PathTracer tracerInput      = new PathTracer("[Input]");
+    private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
+    private final PathTracer tracerFiller     = new PathTracer("Filler");
+    private final PathTracer tracerStroker    = new PathTracer("Stroker");
+
+    DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
+        // used by RendererContext
+        this.rdrCtx = rdrCtx;
+        this.cpDetector = new ClosedPathDetector(rdrCtx);
+        this.pathClipper = new PathClipFilter(rdrCtx);
+    }
+
     public DPathConsumer2D wrapPath2D(Path2D p2d) {
         return wp_Path2DWrapper.init(p2d);
     }
 
+    public DPathConsumer2D traceInput(DPathConsumer2D out) {
+        return tracerInput.init(out);
+    }
+
+    public DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
+        return tracerCPDetector.init(out);
+    }
+
+    public DPathConsumer2D traceFiller(DPathConsumer2D out) {
+        return tracerFiller.init(out);
+    }
+
+    public DPathConsumer2D traceStroker(DPathConsumer2D out) {
+        return tracerStroker.init(out);
+    }
+
+    public DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
+        return cpDetector.init(out);
+    }
+
+    public DPathConsumer2D pathClipper(DPathConsumer2D out,
+                                       final double rdrOffX,
+                                       final double rdrOffY)
+    {
+        return pathClipper.init(out, rdrOffX, rdrOffY);
+    }
+
     public DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
-                                                 BaseTransform at)
+                                                  BaseTransform at,
+                                                  final double rdrOffX,
+                                                  final double rdrOffY)
     {
         if (at == null) {
             return out;
         }
-        double mxx = at.getMxx();
-        double mxy = at.getMxy();
-        double myx = at.getMyx();
-        double myy = at.getMyy();
+        final double mxx = at.getMxx();
+        final double mxy = at.getMxy();
+        final double myx = at.getMyx();
+        final double myy = at.getMyy();
 
         if (mxy == 0.0d && myx == 0.0d) {
             if (mxx == 1.0d && myy == 1.0d) {
                 return out;
             } else {
+                // Scale only
+                if (rdrCtx.doClip) {
+                    // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                    adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                    adjustClipScale(rdrCtx.clipRect, mxx, myy);
+                }
                 return dt_DeltaScaleFilter.init(out, mxx, myy);
             }
         } else {
+            if (rdrCtx.doClip) {
+                // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
+            }
             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
         }
     }
 
-    // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
-    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
-    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+    private static void adjustClipOffset(final double[] clipRect,
+                                         final double rdrOffX,
+                                         final double rdrOffY)
+    {
+        clipRect[0] += rdrOffY;
+        clipRect[1] += rdrOffY;
+        clipRect[2] += rdrOffX;
+        clipRect[3] += rdrOffX;
+    }
+
+    private static void adjustClipScale(final double[] clipRect,
+                                        final double mxx, final double myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaScaleFilter):
+        clipRect[0] /= myy;
+        clipRect[1] /= myy;
+        clipRect[2] /= mxx;
+        clipRect[3] /= mxx;
+    }
+
+    private static void adjustClipInverseDelta(final double[] clipRect,
+                                               final double mxx, final double mxy,
+                                               final double myx, final double myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaTransformFilter):
+        final double det = mxx * myy - mxy * myx;
+        final double imxx =  myy / det;
+        final double imxy = -mxy / det;
+        final double imyx = -myx / det;
+        final double imyy =  mxx / det;
+
+        double xmin, xmax, ymin, ymax;
+        double x, y;
+        // xmin, ymin:
+        x = clipRect[2] * imxx + clipRect[0] * imxy;
+        y = clipRect[2] * imyx + clipRect[0] * imyy;
+
+        xmin = xmax = x;
+        ymin = ymax = y;
+
+        // xmax, ymin:
+        x = clipRect[3] * imxx + clipRect[0] * imxy;
+        y = clipRect[3] * imyx + clipRect[0] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmin, ymax:
+        x = clipRect[2] * imxx + clipRect[1] * imxy;
+        y = clipRect[2] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmax, ymax:
+        x = clipRect[3] * imxx + clipRect[1] * imxy;
+        y = clipRect[3] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        clipRect[0] = ymin;
+        clipRect[1] = ymax;
+        clipRect[2] = xmin;
+        clipRect[3] = xmax;
+    }
 
     public DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
                                                         BaseTransform at)
@@ -89,7 +215,7 @@
                 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
             }
         } else {
-            double det = mxx * myy - mxy * myx;
+            final double det = mxx * myy - mxy * myx;
             return iv_DeltaTransformFilter.init(out,
                                                 myy / det,
                                                -mxy / det,
@@ -258,4 +384,412 @@
             p2d.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
         }
     }
+
+    static final class ClosedPathDetector implements DPathConsumer2D {
+
+        private final DRendererContext rdrCtx;
+        private final PolyStack stack;
+
+        private DPathConsumer2D out;
+
+        ClosedPathDetector(final DRendererContext rdrCtx) {
+            this.rdrCtx = rdrCtx;
+            this.stack = (rdrCtx.stats != null) ?
+                new PolyStack(rdrCtx,
+                        rdrCtx.stats.stat_cpd_polystack_types,
+                        rdrCtx.stats.stat_cpd_polystack_curves,
+                        rdrCtx.stats.hist_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_types)
+                : new PolyStack(rdrCtx);
+        }
+
+        ClosedPathDetector init(DPathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        /**
+         * Disposes this instance:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            stack.dispose();
+        }
+
+        @Override
+        public void pathDone() {
+            // previous path is not closed:
+            finish(false);
+            out.pathDone();
+
+            // TODO: fix possible leak if exception happened
+            // Dispose this instance:
+            dispose();
+        }
+
+        @Override
+        public void closePath() {
+            // path is closed
+            finish(true);
+            out.closePath();
+        }
+
+        @Override
+        public void moveTo(double x0, double y0) {
+            // previous path is not closed:
+            finish(false);
+            out.moveTo(x0, y0);
+        }
+
+        private void finish(final boolean closed) {
+            rdrCtx.closedPath = closed;
+            stack.pullAll(out);
+        }
+
+        @Override
+        public void lineTo(double x1, double y1) {
+            stack.pushLine(x1, y1);
+        }
+
+        @Override
+        public void curveTo(double x3, double y3,
+                            double x2, double y2,
+                            double x1, double y1)
+        {
+            stack.pushCubic(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(double x2, double y2, double x1, double y1) {
+            stack.pushQuad(x1, y1, x2, y2);
+        }
+    }
+
+    static final class PathClipFilter implements DPathConsumer2D {
+
+        private DPathConsumer2D out;
+
+        // Bounds of the drawing region, at pixel precision.
+        private final double[] clipRect;
+
+        private final double[] corners = new double[8];
+        private boolean init_corners = false;
+
+        private final IndexStack stack;
+
+        // the current outcode of the current sub path
+        private int cOutCode = 0;
+
+        // the cumulated (and) outcode of the complete path
+        private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
+
+        private boolean outside = false;
+
+        // The current point OUTSIDE
+        private double cx0, cy0;
+
+        PathClipFilter(final DRendererContext rdrCtx) {
+            this.clipRect = rdrCtx.clipRect;
+            this.stack = (rdrCtx.stats != null) ?
+                new IndexStack(rdrCtx,
+                        rdrCtx.stats.stat_pcf_idxstack_indices,
+                        rdrCtx.stats.hist_pcf_idxstack_indices,
+                        rdrCtx.stats.stat_array_pcf_idxstack_indices)
+                : new IndexStack(rdrCtx);
+        }
+
+        PathClipFilter init(final DPathConsumer2D out,
+                            final double rdrOffX,
+                            final double rdrOffY)
+        {
+            this.out = out;
+
+            // add a small rounding error:
+            final double margin = 1e-3d;
+
+            final double[] _clipRect = this.clipRect;
+            // Adjust the clipping rectangle with the renderer offsets
+            _clipRect[0] -= margin - rdrOffY;
+            _clipRect[1] += margin + rdrOffY;
+            _clipRect[2] -= margin - rdrOffX;
+            _clipRect[3] += margin + rdrOffX;
+
+            this.init_corners = true;
+            this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
+
+            return this; // fluent API
+        }
+
+        /**
+         * Disposes this instance:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            stack.dispose();
+        }
+
+        private void finishPath() {
+            if (outside) {
+                // criteria: inside or totally outside ?
+                if (gOutCode == 0) {
+                    finish();
+                } else {
+                    this.outside = false;
+                    stack.reset();
+                }
+            }
+        }
+
+        private void finish() {
+            this.outside = false;
+
+            if (!stack.isEmpty()) {
+                if (init_corners) {
+                    init_corners = false;
+
+                    final double[] _corners = corners;
+                    final double[] _clipRect = clipRect;
+                    // Top Left (0):
+                    _corners[0] = _clipRect[2];
+                    _corners[1] = _clipRect[0];
+                    // Bottom Left (1):
+                    _corners[2] = _clipRect[2];
+                    _corners[3] = _clipRect[1];
+                    // Top right (2):
+                    _corners[4] = _clipRect[3];
+                    _corners[5] = _clipRect[0];
+                    // Bottom Right (3):
+                    _corners[6] = _clipRect[3];
+                    _corners[7] = _clipRect[1];
+                }
+                stack.pullAll(corners, out);
+            }
+            out.lineTo(cx0, cy0);
+        }
+
+        @Override
+        public void pathDone() {
+            finishPath();
+
+            out.pathDone();
+
+            // TODO: fix possible leak if exception happened
+            // Dispose this instance:
+            dispose();
+        }
+
+        @Override
+        public void closePath() {
+            finishPath();
+
+            out.closePath();
+        }
+
+        @Override
+        public void moveTo(final double x0, final double y0) {
+            finishPath();
+
+            final int outcode = DHelpers.outcode(x0, y0, clipRect);
+            this.cOutCode = outcode;
+            this.outside = false;
+            out.moveTo(x0, y0);
+        }
+
+        @Override
+        public void lineTo(final double xe, final double ye) {
+            final int outcode0 = this.cOutCode;
+            final int outcode1 = DHelpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode1;
+
+            final int sideCode = (outcode0 & outcode1);
+
+            // basic rejection criteria:
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                this.gOutCode &= sideCode;
+                // keep last point coordinate before entering the clip again:
+                this.outside = true;
+                this.cx0 = xe;
+                this.cy0 = ye;
+
+                clip(sideCode, outcode0, outcode1);
+                return;
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.lineTo(xe, ye);
+        }
+
+        private void clip(final int sideCode,
+                          final int outcode0,
+                          final int outcode1)
+        {
+            // corner or cross-boundary on left or right side:
+            if ((outcode0 != outcode1)
+                    && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0))
+            {
+                // combine outcodes:
+                final int mergeCode = (outcode0 | outcode1);
+                final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B;
+                final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R;
+                final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2;
+
+                // add corners to outside stack:
+                switch (tbCode) {
+                    case MarlinConst.OUTCODE_TOP:
+// System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                        stack.push(off); // top
+                        return;
+                    case MarlinConst.OUTCODE_BOTTOM:
+// System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                        stack.push(off + 1); // bottom
+                        return;
+                    default:
+                        // both TOP / BOTTOM:
+                        if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
+// System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                            // top to bottom
+                            stack.push(off); // top
+                            stack.push(off + 1); // bottom
+                        } else {
+// System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                            // bottom to top
+                            stack.push(off + 1); // bottom
+                            stack.push(off); // top
+                        }
+                }
+            }
+        }
+
+        @Override
+        public void curveTo(final double x1, final double y1,
+                            final double x2, final double y2,
+                            final double xe, final double ye)
+        {
+            final int outcode0 = this.cOutCode;
+            final int outcode3 = DHelpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode3;
+
+            int sideCode = outcode0 & outcode3;
+
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                sideCode &= DHelpers.outcode(x1, y1, clipRect);
+                sideCode &= DHelpers.outcode(x2, y2, clipRect);
+                this.gOutCode &= sideCode;
+
+                // basic rejection criteria:
+                if (sideCode != 0) {
+                    // keep last point coordinate before entering the clip again:
+                    this.outside = true;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
+
+                    clip(sideCode, outcode0, outcode3);
+                    return;
+                }
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.curveTo(x1, y1, x2, y2, xe, ye);
+        }
+
+        @Override
+        public void quadTo(final double x1, final double y1,
+                           final double xe, final double ye)
+        {
+            final int outcode0 = this.cOutCode;
+            final int outcode2 = DHelpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode2;
+
+            int sideCode = outcode0 & outcode2;
+
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                sideCode &= DHelpers.outcode(x1, y1, clipRect);
+                this.gOutCode &= sideCode;
+
+                // basic rejection criteria:
+                if (sideCode != 0) {
+                    // keep last point coordinate before entering the clip again:
+                    this.outside = true;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
+
+                    clip(sideCode, outcode0, outcode2);
+                    return;
+                }
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.quadTo(x1, y1, xe, ye);
+        }
+    }
+
+    static final class PathTracer implements DPathConsumer2D {
+        private final String prefix;
+        private DPathConsumer2D out;
+
+        PathTracer(String name) {
+            this.prefix = name + ": ";
+        }
+
+        PathTracer init(DPathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        @Override
+        public void moveTo(double x0, double y0) {
+            log("moveTo (" + x0 + ", " + y0 + ')');
+            out.moveTo(x0, y0);
+        }
+
+        @Override
+        public void lineTo(double x1, double y1) {
+            log("lineTo (" + x1 + ", " + y1 + ')');
+            out.lineTo(x1, y1);
+        }
+
+        @Override
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
+        {
+            log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
+            out.curveTo(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(double x1, double y1, double x2, double y2) {
+            log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
+            out.quadTo(x1, y1, x2, y2);
+        }
+
+        @Override
+        public void closePath() {
+            log("closePath");
+            out.closePath();
+        }
+
+        @Override
+        public void pathDone() {
+            log("pathDone");
+            out.pathDone();
+        }
+
+        private void log(final String message) {
+            System.out.println(prefix + message);
+        }
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Dasher.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Dasher.java	Wed Dec 20 12:22:56 2017 -0800
@@ -138,7 +138,7 @@
                     dashOn = !dashOn;
                 }
             }
-        } else if (phase > 0) {
+        } else if (phase > 0.0f) {
             if (cycles >= MAX_CYCLES) {
                 phase = 0.0f;
             } else {
@@ -158,12 +158,13 @@
 
         this.dash = dash;
         this.dashLen = dashLen;
-        this.startPhase = this.phase = phase;
+        this.phase = phase;
+        this.startPhase = phase;
         this.startDashOn = dashOn;
         this.startIdx = sidx;
         this.starting = true;
-        needsMoveTo = false;
-        firstSegidx = 0;
+        this.needsMoveTo = false;
+        this.firstSegidx = 0;
 
         this.recycleDashes = recycleDashes;
 
@@ -202,8 +203,8 @@
     }
 
     @Override
-    public void moveTo(float x0, float y0) {
-        if (firstSegidx > 0) {
+    public void moveTo(final float x0, final float y0) {
+        if (firstSegidx != 0) {
             out.moveTo(sx, sy);
             emitFirstSegments();
         }
@@ -211,8 +212,10 @@
         this.idx = startIdx;
         this.dashOn = this.startDashOn;
         this.phase = this.startPhase;
-        this.sx = this.x0 = x0;
-        this.sy = this.y0 = y0;
+        this.sx = x0;
+        this.sy = y0;
+        this.x0 = x0;
+        this.y0 = y0;
         this.starting = true;
     }
 
@@ -237,7 +240,7 @@
     private void emitFirstSegments() {
         final float[] fSegBuf = firstSegmentsBuffer;
 
-        for (int i = 0; i < firstSegidx; ) {
+        for (int i = 0, len = firstSegidx; i < len; ) {
             int type = (int)fSegBuf[i];
             emitSeg(fSegBuf, i + 1, type);
             i += (type - 1);
@@ -252,48 +255,59 @@
     private int firstSegidx;
 
     // precondition: pts must be in relative coordinates (relative to x0,y0)
-    private void goTo(float[] pts, int off, final int type) {
-        float x = pts[off + type - 4];
-        float y = pts[off + type - 3];
-        if (dashOn) {
+    private void goTo(final float[] pts, final int off, final int type,
+                      final boolean on)
+    {
+        final int index = off + type;
+        final float x = pts[index - 4];
+        final float y = pts[index - 3];
+
+        if (on) {
             if (starting) {
-                int len = type - 1; // - 2 + 1
-                int segIdx = firstSegidx;
-                float[] buf = firstSegmentsBuffer;
-                if (segIdx + len  > buf.length) {
-                    if (DO_STATS) {
-                        rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
-                            .add(segIdx + len);
-                    }
-                    firstSegmentsBuffer = buf
-                        = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
-                                                             segIdx + len);
-                }
-                buf[segIdx++] = type;
-                len--;
-                // small arraycopy (2, 4 or 6) but with offset:
-                System.arraycopy(pts, off, buf, segIdx, len);
-                segIdx += len;
-                firstSegidx = segIdx;
+                goTo_starting(pts, off, type);
             } else {
                 if (needsMoveTo) {
+                    needsMoveTo = false;
                     out.moveTo(x0, y0);
-                    needsMoveTo = false;
                 }
                 emitSeg(pts, off, type);
             }
         } else {
-            starting = false;
+            if (starting) {
+                // low probability test (hotspot)
+                starting = false;
+            }
             needsMoveTo = true;
         }
         this.x0 = x;
         this.y0 = y;
     }
 
+    private void goTo_starting(final float[] pts, final int off, final int type) {
+        int len = type - 1; // - 2 + 1
+        int segIdx = firstSegidx;
+        float[] buf = firstSegmentsBuffer;
+
+        if (segIdx + len  > buf.length) {
+            if (DO_STATS) {
+                rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
+                    .add(segIdx + len);
+            }
+            firstSegmentsBuffer = buf
+                = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
+                                                     segIdx + len);
+        }
+        buf[segIdx++] = type;
+        len--;
+        // small arraycopy (2, 4 or 6) but with offset:
+        System.arraycopy(pts, off, buf, segIdx, len);
+        firstSegidx = segIdx + len;
+    }
+
     @Override
-    public void lineTo(float x1, float y1) {
-        float dx = x1 - x0;
-        float dy = y1 - y0;
+    public void lineTo(final float x1, final float y1) {
+        final float dx = x1 - x0;
+        final float dy = y1 - y0;
 
         float len = dx*dx + dy*dy;
         if (len == 0.0f) {
@@ -308,48 +322,61 @@
 
         final float[] _curCurvepts = curCurvepts;
         final float[] _dash = dash;
+        final int _dashLen = this.dashLen;
+
+        int _idx = idx;
+        boolean _dashOn = dashOn;
+        float _phase = phase;
 
         float leftInThisDashSegment;
-        float dashdx, dashdy, p;
+        float d, dashdx, dashdy, p;
 
         while (true) {
-            leftInThisDashSegment = _dash[idx] - phase;
+            d = _dash[_idx];
+            leftInThisDashSegment = d - _phase;
 
             if (len <= leftInThisDashSegment) {
                 _curCurvepts[0] = x1;
                 _curCurvepts[1] = y1;
-                goTo(_curCurvepts, 0, 4);
+
+                goTo(_curCurvepts, 0, 4, _dashOn);
 
                 // Advance phase within current dash segment
-                phase += len;
+                _phase += len;
+
                 // TODO: compare float values using epsilon:
                 if (len == leftInThisDashSegment) {
-                    phase = 0.0f;
-                    idx = (idx + 1) % dashLen;
-                    dashOn = !dashOn;
+                    _phase = 0.0f;
+                    _idx = (_idx + 1) % _dashLen;
+                    _dashOn = !_dashOn;
                 }
+
+                // Save local state:
+                idx = _idx;
+                dashOn = _dashOn;
+                phase = _phase;
                 return;
             }
 
-            dashdx = _dash[idx] * cx;
-            dashdy = _dash[idx] * cy;
+            dashdx = d * cx;
+            dashdy = d * cy;
 
-            if (phase == 0.0f) {
+            if (_phase == 0.0f) {
                 _curCurvepts[0] = x0 + dashdx;
                 _curCurvepts[1] = y0 + dashdy;
             } else {
-                p = leftInThisDashSegment / _dash[idx];
+                p = leftInThisDashSegment / d;
                 _curCurvepts[0] = x0 + p * dashdx;
                 _curCurvepts[1] = y0 + p * dashdy;
             }
 
-            goTo(_curCurvepts, 0, 4);
+            goTo(_curCurvepts, 0, 4, _dashOn);
 
             len -= leftInThisDashSegment;
             // Advance to next dash segment
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
-            phase = 0.0f;
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
+            _phase = 0.0f;
         }
     }
 
@@ -358,43 +385,59 @@
 
     // preconditions: curCurvepts must be an array of length at least 2 * type,
     // that contains the curve we want to dash in the first type elements
-    private void somethingTo(int type) {
+    private void somethingTo(final int type) {
         if (pointCurve(curCurvepts, type)) {
             return;
         }
-        li.initializeIterationOnCurve(curCurvepts, type);
+        final LengthIterator _li = li;
+        final float[] _curCurvepts = curCurvepts;
+        final float[] _dash = dash;
+        final int _dashLen = this.dashLen;
+
+        _li.initializeIterationOnCurve(_curCurvepts, type);
+
+        int _idx = idx;
+        boolean _dashOn = dashOn;
+        float _phase = phase;
 
         // initially the current curve is at curCurvepts[0...type]
         int curCurveoff = 0;
         float lastSplitT = 0.0f;
         float t;
-        float leftInThisDashSegment = dash[idx] - phase;
+        float leftInThisDashSegment = _dash[_idx] - _phase;
 
-        while ((t = li.next(leftInThisDashSegment)) < 1.0f) {
+        while ((t = _li.next(leftInThisDashSegment)) < 1.0f) {
             if (t != 0.0f) {
                 Helpers.subdivideAt((t - lastSplitT) / (1.0f - lastSplitT),
-                                    curCurvepts, curCurveoff,
-                                    curCurvepts, 0,
-                                    curCurvepts, type, type);
+                                    _curCurvepts, curCurveoff,
+                                    _curCurvepts, 0,
+                                    _curCurvepts, type, type);
                 lastSplitT = t;
-                goTo(curCurvepts, 2, type);
+                goTo(_curCurvepts, 2, type, _dashOn);
                 curCurveoff = type;
             }
             // Advance to next dash segment
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
-            phase = 0.0f;
-            leftInThisDashSegment = dash[idx];
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
+            _phase = 0.0f;
+            leftInThisDashSegment = _dash[_idx];
         }
-        goTo(curCurvepts, curCurveoff+2, type);
-        phase += li.lastSegLen();
-        if (phase >= dash[idx]) {
-            phase = 0.0f;
-            idx = (idx + 1) % dashLen;
-            dashOn = !dashOn;
+
+        goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
+
+        _phase += _li.lastSegLen();
+        if (_phase >= _dash[_idx]) {
+            _phase = 0.0f;
+            _idx = (_idx + 1) % _dashLen;
+            _dashOn = !_dashOn;
         }
+        // Save local state:
+        idx = _idx;
+        dashOn = _dashOn;
+        phase = _phase;
+
         // reset LengthIterator:
-        li.reset();
+        _li.reset();
     }
 
     private static boolean pointCurve(float[] curve, int type) {
@@ -420,7 +463,7 @@
     // tree; however, the trees we are interested in have the property that
     // every non leaf node has exactly 2 children
     static final class LengthIterator {
-        private enum Side {LEFT, RIGHT};
+        private enum Side {LEFT, RIGHT}
         // Holds the curves at various levels of the recursion. The root
         // (i.e. the original curve) is at recCurveStack[0] (but then it
         // gets subdivided, the left half is put at 1, so most of the time
@@ -670,22 +713,23 @@
         // this is a bit of a hack. It returns -1 if we're not on a leaf, and
         // the length of the leaf if we are on a leaf.
         private float onLeaf() {
-            float[] curve = recCurveStack[recLevel];
+            final float[] curve = recCurveStack[recLevel];
+            final int _curveType = curveType;
             float polyLen = 0.0f;
 
             float x0 = curve[0], y0 = curve[1];
-            for (int i = 2; i < curveType; i += 2) {
+            for (int i = 2; i < _curveType; i += 2) {
                 final float x1 = curve[i], y1 = curve[i+1];
                 final float len = Helpers.linelen(x0, y0, x1, y1);
                 polyLen += len;
-                curLeafCtrlPolyLengths[i/2 - 1] = len;
+                curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
                 x0 = x1;
                 y0 = y1;
             }
 
             final float lineLen = Helpers.linelen(curve[0], curve[1],
-                                                  curve[curveType-2],
-                                                  curve[curveType-1]);
+                                                  curve[_curveType-2],
+                                                  curve[_curveType-1]);
             if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
                 return (polyLen + lineLen) / 2.0f;
             }
@@ -694,9 +738,9 @@
     }
 
     @Override
-    public void curveTo(float x1, float y1,
-                        float x2, float y2,
-                        float x3, float y3)
+    public void curveTo(final float x1, final float y1,
+                        final float x2, final float y2,
+                        final float x3, final float y3)
     {
         final float[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = x0;        _curCurvepts[1] = y0;
@@ -707,7 +751,9 @@
     }
 
     @Override
-    public void quadTo(float x1, float y1, float x2, float y2) {
+    public void quadTo(final float x1, final float y1,
+                       final float x2, final float y2)
+    {
         final float[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = x0;        _curCurvepts[1] = y0;
         _curCurvepts[2] = x1;        _curCurvepts[3] = y1;
@@ -718,7 +764,7 @@
     @Override
     public void closePath() {
         lineTo(sx, sy);
-        if (firstSegidx > 0) {
+        if (firstSegidx != 0) {
             if (!dashOn || needsMoveTo) {
                 out.moveTo(sx, sy);
             }
@@ -729,7 +775,7 @@
 
     @Override
     public void pathDone() {
-        if (firstSegidx > 0) {
+        if (firstSegidx != 0) {
             out.moveTo(sx, sy);
             emitFirstSegments();
         }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Helpers.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Helpers.java	Wed Dec 20 12:22:56 2017 -0800
@@ -25,11 +25,11 @@
 
 package com.sun.marlin;
 
+import com.sun.javafx.geom.PathConsumer2D;
 import static java.lang.Math.PI;
-import static java.lang.Math.cos;
-import static java.lang.Math.sqrt;
-import static java.lang.Math.cbrt;
-import static java.lang.Math.acos;
+import java.util.Arrays;
+import com.sun.marlin.stats.Histogram;
+import com.sun.marlin.stats.StatLong;
 
 final class Helpers implements MarlinConst {
 
@@ -120,17 +120,17 @@
         int num;
         if (D < 0.0d) {
             // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
-            final double phi = (1.0d/3.0d) * acos(-q / sqrt(-cb_p));
-            final double t = 2.0d * sqrt(-p);
+            final double phi = (1.0d/3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
+            final double t = 2.0d * Math.sqrt(-p);
 
-            pts[ off+0 ] = (float) ( t * cos(phi));
-            pts[ off+1 ] = (float) (-t * cos(phi + (PI / 3.0d)));
-            pts[ off+2 ] = (float) (-t * cos(phi - (PI / 3.0d)));
+            pts[ off+0 ] = (float) ( t * Math.cos(phi));
+            pts[ off+1 ] = (float) (-t * Math.cos(phi + (PI / 3.0d)));
+            pts[ off+2 ] = (float) (-t * Math.cos(phi - (PI / 3.0d)));
             num = 3;
         } else {
-            final double sqrt_D = sqrt(D);
-            final double u = cbrt(sqrt_D - q);
-            final double v = - cbrt(sqrt_D + q);
+            final double sqrt_D = Math.sqrt(D);
+            final double u =   Math.cbrt(sqrt_D - q);
+            final double v = - Math.cbrt(sqrt_D + q);
 
             pts[ off ] = (float) (u + v);
             num = 1;
@@ -176,15 +176,6 @@
         return ret;
     }
 
-    static float polyLineLength(float[] poly, final int off, final int nCoords) {
-        assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
-        float acc = 0.0f;
-        for (int i = off + 2; i < off + nCoords; i += 2) {
-            acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
-        }
-        return acc;
-    }
-
     static float linelen(float x1, float y1, float x2, float y2) {
         final float dx = x2 - x1;
         final float dy = y2 - y1;
@@ -438,4 +429,388 @@
             return;
         }
     }
+
+    // From sun.java2d.loops.GeneralRenderer:
+
+    static int outcode(final float x, final float y,
+                       final float[] clipRect)
+    {
+        int code;
+        if (y < clipRect[0]) {
+            code = OUTCODE_TOP;
+        } else if (y >= clipRect[1]) {
+            code = OUTCODE_BOTTOM;
+        } else {
+            code = 0;
+        }
+        if (x < clipRect[2]) {
+            code |= OUTCODE_LEFT;
+        } else if (x >= clipRect[3]) {
+            code |= OUTCODE_RIGHT;
+        }
+        return code;
+    }
+
+    // a stack of polynomial curves where each curve shares endpoints with
+    // adjacent ones.
+    static final class PolyStack {
+        private static final byte TYPE_LINETO  = (byte) 0;
+        private static final byte TYPE_QUADTO  = (byte) 1;
+        private static final byte TYPE_CUBICTO = (byte) 2;
+
+        // curves capacity = edges count (8192) = edges x 2 (coords)
+        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
+
+        // types capacity = edges count (4096)
+        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
+
+        float[] curves;
+        int end;
+        byte[] curveTypes;
+        int numCurves;
+
+        // curves ref (dirty)
+        final FloatArrayCache.Reference curves_ref;
+        // curveTypes ref (dirty)
+        final ByteArrayCache.Reference curveTypes_ref;
+
+        // used marks (stats only)
+        int curveTypesUseMark;
+        int curvesUseMark;
+
+        private final StatLong stat_polystack_types;
+        private final StatLong stat_polystack_curves;
+        private final Histogram hist_polystack_curves;
+        private final StatLong stat_array_polystack_curves;
+        private final StatLong stat_array_polystack_curveTypes;
+
+        PolyStack(final RendererContext rdrCtx) {
+            this(rdrCtx, null, null, null, null, null);
+        }
+
+        PolyStack(final RendererContext rdrCtx,
+                  final StatLong stat_polystack_types,
+                  final StatLong stat_polystack_curves,
+                  final Histogram hist_polystack_curves,
+                  final StatLong stat_array_polystack_curves,
+                  final StatLong stat_array_polystack_curveTypes)
+        {
+            curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
+            curves     = curves_ref.initial;
+
+            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
+            curveTypes     = curveTypes_ref.initial;
+            numCurves = 0;
+            end = 0;
+
+            if (DO_STATS) {
+                curveTypesUseMark = 0;
+                curvesUseMark = 0;
+            }
+            this.stat_polystack_types = stat_polystack_types;
+            this.stat_polystack_curves = stat_polystack_curves;
+            this.hist_polystack_curves = hist_polystack_curves;
+            this.stat_array_polystack_curves = stat_array_polystack_curves;
+            this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;
+        }
+
+        /**
+         * Disposes this PolyStack:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            end = 0;
+            numCurves = 0;
+
+            if (DO_STATS) {
+                stat_polystack_types.add(curveTypesUseMark);
+                stat_polystack_curves.add(curvesUseMark);
+                hist_polystack_curves.add(curvesUseMark);
+
+                // reset marks
+                curveTypesUseMark = 0;
+                curvesUseMark = 0;
+            }
+
+            // Return arrays:
+            // curves and curveTypes are kept dirty
+            curves     = curves_ref.putArray(curves);
+            curveTypes = curveTypes_ref.putArray(curveTypes);
+        }
+
+        private void ensureSpace(final int n) {
+            // use substraction to avoid integer overflow:
+            if (curves.length - end < n) {
+                if (DO_STATS) {
+                    stat_array_polystack_curves.add(end + n);
+                }
+                curves = curves_ref.widenArray(curves, end, end + n);
+            }
+            if (curveTypes.length <= numCurves) {
+                if (DO_STATS) {
+                    stat_array_polystack_curveTypes.add(numCurves + 1);
+                }
+                curveTypes = curveTypes_ref.widenArray(curveTypes,
+                                                       numCurves,
+                                                       numCurves + 1);
+            }
+        }
+
+        void pushCubic(float x0, float y0,
+                       float x1, float y1,
+                       float x2, float y2)
+        {
+            ensureSpace(6);
+            curveTypes[numCurves++] = TYPE_CUBICTO;
+            // we reverse the coordinate order to make popping easier
+            final float[] _curves = curves;
+            int e = end;
+            _curves[e++] = x2;    _curves[e++] = y2;
+            _curves[e++] = x1;    _curves[e++] = y1;
+            _curves[e++] = x0;    _curves[e++] = y0;
+            end = e;
+        }
+
+        void pushQuad(float x0, float y0,
+                      float x1, float y1)
+        {
+            ensureSpace(4);
+            curveTypes[numCurves++] = TYPE_QUADTO;
+            final float[] _curves = curves;
+            int e = end;
+            _curves[e++] = x1;    _curves[e++] = y1;
+            _curves[e++] = x0;    _curves[e++] = y0;
+            end = e;
+        }
+
+        void pushLine(float x, float y) {
+            ensureSpace(2);
+            curveTypes[numCurves++] = TYPE_LINETO;
+            curves[end++] = x;    curves[end++] = y;
+        }
+
+        void pullAll(final PathConsumer2D io) {
+            final int nc = numCurves;
+            if (nc == 0) {
+                return;
+            }
+            if (DO_STATS) {
+                // update used marks:
+                if (numCurves > curveTypesUseMark) {
+                    curveTypesUseMark = numCurves;
+                }
+                if (end > curvesUseMark) {
+                    curvesUseMark = end;
+                }
+            }
+            final byte[]  _curveTypes = curveTypes;
+            final float[] _curves = curves;
+            int e = 0;
+
+            for (int i = 0; i < nc; i++) {
+                switch(_curveTypes[i]) {
+                case TYPE_LINETO:
+                    io.lineTo(_curves[e], _curves[e+1]);
+                    e += 2;
+                    continue;
+                case TYPE_QUADTO:
+                    io.quadTo(_curves[e+0], _curves[e+1],
+                              _curves[e+2], _curves[e+3]);
+                    e += 4;
+                    continue;
+                case TYPE_CUBICTO:
+                    io.curveTo(_curves[e+0], _curves[e+1],
+                               _curves[e+2], _curves[e+3],
+                               _curves[e+4], _curves[e+5]);
+                    e += 6;
+                    continue;
+                default:
+                }
+            }
+            numCurves = 0;
+            end = 0;
+        }
+
+        void popAll(final PathConsumer2D io) {
+            int nc = numCurves;
+            if (nc == 0) {
+                return;
+            }
+            if (DO_STATS) {
+                // update used marks:
+                if (numCurves > curveTypesUseMark) {
+                    curveTypesUseMark = numCurves;
+                }
+                if (end > curvesUseMark) {
+                    curvesUseMark = end;
+                }
+            }
+            final byte[]  _curveTypes = curveTypes;
+            final float[] _curves = curves;
+            int e  = end;
+
+            while (nc != 0) {
+                switch(_curveTypes[--nc]) {
+                case TYPE_LINETO:
+                    e -= 2;
+                    io.lineTo(_curves[e], _curves[e+1]);
+                    continue;
+                case TYPE_QUADTO:
+                    e -= 4;
+                    io.quadTo(_curves[e+0], _curves[e+1],
+                              _curves[e+2], _curves[e+3]);
+                    continue;
+                case TYPE_CUBICTO:
+                    e -= 6;
+                    io.curveTo(_curves[e+0], _curves[e+1],
+                               _curves[e+2], _curves[e+3],
+                               _curves[e+4], _curves[e+5]);
+                    continue;
+                default:
+                }
+            }
+            numCurves = 0;
+            end = 0;
+        }
+
+        @Override
+        public String toString() {
+            String ret = "";
+            int nc = numCurves;
+            int last = end;
+            int len;
+            while (nc != 0) {
+                switch(curveTypes[--nc]) {
+                case TYPE_LINETO:
+                    len = 2;
+                    ret += "line: ";
+                    break;
+                case TYPE_QUADTO:
+                    len = 4;
+                    ret += "quad: ";
+                    break;
+                case TYPE_CUBICTO:
+                    len = 6;
+                    ret += "cubic: ";
+                    break;
+                default:
+                    len = 0;
+                }
+                last -= len;
+                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
+                                       + "\n";
+            }
+            return ret;
+        }
+    }
+
+    // a stack of integer indices
+    static final class IndexStack {
+
+        // integer capacity = edges count / 4 ~ 1024
+        private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2;
+
+        private int end;
+        private int[] indices;
+
+        // indices ref (dirty)
+        private final IntArrayCache.Reference indices_ref;
+
+        // used marks (stats only)
+        private int indicesUseMark;
+
+        private final StatLong stat_idxstack_indices;
+        private final Histogram hist_idxstack_indices;
+        private final StatLong stat_array_idxstack_indices;
+
+        IndexStack(final RendererContext rdrCtx) {
+            this(rdrCtx, null, null, null);
+        }
+
+        IndexStack(final RendererContext rdrCtx,
+                   final StatLong stat_idxstack_indices,
+                   final Histogram hist_idxstack_indices,
+                   final StatLong stat_array_idxstack_indices)
+        {
+            indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K
+            indices     = indices_ref.initial;
+            end = 0;
+
+            if (DO_STATS) {
+                indicesUseMark = 0;
+            }
+            this.stat_idxstack_indices = stat_idxstack_indices;
+            this.hist_idxstack_indices = hist_idxstack_indices;
+            this.stat_array_idxstack_indices = stat_array_idxstack_indices;
+        }
+
+        /**
+         * Disposes this PolyStack:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            end = 0;
+
+            if (DO_STATS) {
+                stat_idxstack_indices.add(indicesUseMark);
+                hist_idxstack_indices.add(indicesUseMark);
+
+                // reset marks
+                indicesUseMark = 0;
+            }
+
+            // Return arrays:
+            // values is kept dirty
+            indices = indices_ref.putArray(indices);
+        }
+
+        boolean isEmpty() {
+            return (end == 0);
+        }
+
+        void reset() {
+            end = 0;
+        }
+
+        void push(final int v) {
+            // remove redundant values (reverse order):
+            int[] _values = indices;
+            final int nc = end;
+            if (nc != 0) {
+                if (_values[nc - 1] == v) {
+                    // remove both duplicated values:
+                    end--;
+                    return;
+                }
+            }
+            if (_values.length <= nc) {
+                if (DO_STATS) {
+                    stat_array_idxstack_indices.add(nc + 1);
+                }
+                indices = _values = indices_ref.widenArray(_values, nc, nc + 1);
+            }
+            _values[end++] = v;
+
+            if (DO_STATS) {
+                // update used marks:
+                if (end > indicesUseMark) {
+                    indicesUseMark = end;
+                }
+            }
+        }
+
+        void pullAll(final float[] points, final PathConsumer2D io) {
+            final int nc = end;
+            if (nc == 0) {
+                return;
+            }
+            final int[] _values = indices;
+
+            for (int i = 0, j; i < nc; i++) {
+                j = _values[i] << 1;
+                io.lineTo(points[j], points[j + 1]);
+            }
+            end = 0;
+        }
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinConst.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinConst.java	Wed Dec 20 12:22:56 2017 -0800
@@ -138,4 +138,47 @@
     // minimum width to try using RLE encoding:
     static final int RLE_MIN_WIDTH
         = Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth());
+
+    // Constants
+    public static final int WIND_EVEN_ODD = 0;
+    public static final int WIND_NON_ZERO = 1;
+
+    /**
+     * Constant value for join style.
+     */
+    public static final int JOIN_MITER = 0;
+
+    /**
+     * Constant value for join style.
+     */
+    public static final int JOIN_ROUND = 1;
+
+    /**
+     * Constant value for join style.
+     */
+    public static final int JOIN_BEVEL = 2;
+
+    /**
+     * Constant value for end cap style.
+     */
+    public static final int CAP_BUTT = 0;
+
+    /**
+     * Constant value for end cap style.
+     */
+    public static final int CAP_ROUND = 1;
+
+    /**
+     * Constant value for end cap style.
+     */
+    public static final int CAP_SQUARE = 2;
+
+    // Out codes
+    static final int OUTCODE_TOP      = 1;
+    static final int OUTCODE_BOTTOM   = 2;
+    static final int OUTCODE_LEFT     = 4;
+    static final int OUTCODE_RIGHT    = 8;
+    static final int OUTCODE_MASK_T_B = OUTCODE_TOP  | OUTCODE_BOTTOM;
+    static final int OUTCODE_MASK_L_R = OUTCODE_LEFT | OUTCODE_RIGHT;
+    static final int OUTCODE_MASK_T_B_L_R = OUTCODE_MASK_T_B | OUTCODE_MASK_L_R;
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinProperties.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinProperties.java	Wed Dec 20 12:22:56 2017 -0800
@@ -124,6 +124,18 @@
         return getBoolean("prism.marlin.useSimplifier", "false");
     }
 
+    public static boolean isDoClip() {
+        return getBoolean("prism.marlin.clip", "true");
+    }
+
+    public static boolean isDoClipRuntimeFlag() {
+        return getBoolean("prism.marlin.clip.runtime.enable", "false");
+    }
+
+    public static boolean isDoClipAtRuntime() {
+        return getBoolean("prism.marlin.clip.runtime", "true");
+    }
+
     // debugging parameters
 
     public static boolean isDoStats() {
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinRenderer.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinRenderer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,9 +29,6 @@
 
 public interface MarlinRenderer extends PathConsumer2D {
 
-    public static final int WIND_EVEN_ODD = 0;
-    public static final int WIND_NON_ZERO = 1;
-
     public MarlinRenderer init(final int pix_boundsX, final int pix_boundsY,
                                final int pix_boundsWidth, final int pix_boundsHeight,
                                final int windingRule);
@@ -47,4 +44,7 @@
     public int getOutpixMaxY();
 
     public void produceAlphas(MarlinAlphaConsumer ac);
+
+    public float getOffsetX();
+    public float getOffsetY();
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinRenderingEngine.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/MarlinRenderingEngine.java	Wed Dec 20 12:22:56 2017 -0800
@@ -46,14 +46,14 @@
     }
 
     static {
-        if (PathIterator.WIND_NON_ZERO != MarlinRenderer.WIND_NON_ZERO ||
-            PathIterator.WIND_EVEN_ODD != MarlinRenderer.WIND_EVEN_ODD ||
-            BasicStroke.JOIN_MITER != Stroker.JOIN_MITER ||
-            BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND ||
-            BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL ||
-            BasicStroke.CAP_BUTT != Stroker.CAP_BUTT ||
-            BasicStroke.CAP_ROUND != Stroker.CAP_ROUND ||
-            BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE)
+        if (PathIterator.WIND_NON_ZERO != WIND_NON_ZERO ||
+            PathIterator.WIND_EVEN_ODD != WIND_EVEN_ODD ||
+            BasicStroke.JOIN_MITER != JOIN_MITER ||
+            BasicStroke.JOIN_ROUND != JOIN_ROUND ||
+            BasicStroke.JOIN_BEVEL != JOIN_BEVEL ||
+            BasicStroke.CAP_BUTT != CAP_BUTT ||
+            BasicStroke.CAP_ROUND != CAP_ROUND ||
+            BasicStroke.CAP_SQUARE != CAP_SQUARE)
         {
             throw new InternalError("mismatched renderer constants");
         }
@@ -178,6 +178,10 @@
         // optimisation parameters
         logInfo("prism.marlin.useSimplifier    = "
                 + MarlinConst.USE_SIMPLIFIER);
+        logInfo("prism.marlin.clip             = "
+                + MarlinProperties.isDoClip());
+        logInfo("prism.marlin.clip.runtime.enable = "
+                + MarlinProperties.isDoClipRuntimeFlag());
 
         // debugging parameters
         logInfo("prism.marlin.doStats          = "
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Renderer.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Renderer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -43,6 +43,9 @@
     static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
     static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
 
+    private static final float RDR_OFFSET_X = 0.5f / SUBPIXEL_SCALE_X;
+    private static final float RDR_OFFSET_Y = 0.5f / SUBPIXEL_SCALE_Y;
+
     // common to all types of input path segments.
     // OFFSET as bytes
     // only integer values:
@@ -651,7 +654,7 @@
     }
 
     @Override
-    public void moveTo(float pix_x0, float pix_y0) {
+    public void moveTo(final float pix_x0, final float pix_y0) {
         closePath();
         final float sx = tosubpixx(pix_x0);
         final float sy = tosubpixy(pix_y0);
@@ -662,7 +665,7 @@
     }
 
     @Override
-    public void lineTo(float pix_x1, float pix_y1) {
+    public void lineTo(final float pix_x1, final float pix_y1) {
         final float x1 = tosubpixx(pix_x1);
         final float y1 = tosubpixy(pix_y1);
         addLine(x0, y0, x1, y1);
@@ -671,24 +674,26 @@
     }
 
     @Override
-    public void curveTo(float x1, float y1,
-            float x2, float y2,
-            float x3, float y3)
+    public void curveTo(final float pix_x1, final float pix_y1,
+                        final float pix_x2, final float pix_y2,
+                        final float pix_x3, final float pix_y3)
     {
-        final float xe = tosubpixx(x3);
-        final float ye = tosubpixy(y3);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
-                          tosubpixx(x2), tosubpixy(y2), xe, ye);
+        final float xe = tosubpixx(pix_x3);
+        final float ye = tosubpixy(pix_y3);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+                  tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
         curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
     }
 
     @Override
-    public void quadTo(float x1, float y1, float x2, float y2) {
-        final float xe = tosubpixx(x2);
-        final float ye = tosubpixy(y2);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+    public void quadTo(final float pix_x1, final float pix_y1,
+                       final float pix_x2, final float pix_y2)
+    {
+        final float xe = tosubpixx(pix_x2);
+        final float ye = tosubpixy(pix_y2);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
         quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
@@ -696,9 +701,11 @@
 
     @Override
     public void closePath() {
-        addLine(x0, y0, sx0, sy0);
-        x0 = sx0;
-        y0 = sy0;
+        if (x0 != sx0 || y0 != sy0) {
+            addLine(x0, y0, sx0, sy0);
+            x0 = sx0;
+            y0 = sy0;
+        }
     }
 
     @Override
@@ -1471,11 +1478,7 @@
             // ie number of primitives:
 
             // fast check min width:
-            if (width <= RLE_MIN_WIDTH) {
-                useRLE = false;
-            } else {
-                useRLE = true;
-            }
+            useRLE = (width > RLE_MIN_WIDTH);
         }
     }
 
@@ -1552,4 +1555,14 @@
     public int getOutpixMaxY() {
         return bboxY1;
     }
+
+    @Override
+    public float getOffsetX() {
+        return RDR_OFFSET_X;
+    }
+
+    @Override
+    public float getOffsetY() {
+        return RDR_OFFSET_Y;
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererContext.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererContext.java	Wed Dec 20 12:22:56 2017 -0800
@@ -72,6 +72,12 @@
     public final Dasher dasher;
     // flag indicating the shape is stroked (1) or filled (0)
     int stroking = 0;
+    // flag indicating to clip the shape
+    public boolean doClip = false;
+    // flag indicating if the path is closed or not (in advance) to handle properly caps
+    boolean closedPath = false;
+    // clip rectangle (ymin, ymax, xmin, xmax):
+    public final float[] clipRect = new float[4];
 
 // MarlinFX specific:
     // dirty bbox rectangle
@@ -80,14 +86,14 @@
     public MaskMarlinAlphaConsumer consumer = null;
 
     // Array caches:
-    /* clean int[] cache (zero-filled) = 4 refs */
-    private final IntArrayCache cleanIntCache = new IntArrayCache(true, 4);
-    /* dirty int[] cache = 4 refs */
-    private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 4);
-    /* dirty float[] cache = 3 refs */
-    private final FloatArrayCache dirtyFloatCache = new FloatArrayCache(false, 3);
-    /* dirty byte[] cache = 1 ref */
-    private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 1);
+    /* clean int[] cache (zero-filled) = 5 refs */
+    private final IntArrayCache cleanIntCache = new IntArrayCache(true, 5);
+    /* dirty int[] cache = 5 refs */
+    private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 5);
+    /* dirty float[] cache = 4 refs (2 polystack) */
+    private final FloatArrayCache dirtyFloatCache = new FloatArrayCache(false, 4);
+    /* dirty byte[] cache = 2 ref (2 polystack) */
+    private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 2);
 
     // RendererContext statistics
     final RendererStats stats;
@@ -115,7 +121,7 @@
         }
 
         // MarlinRenderingEngine.TransformingPathConsumer2D
-        transformerPC2D = new TransformingPathConsumer2D();
+        transformerPC2D = new TransformingPathConsumer2D(this);
 
         // Renderer shared memory:
         rdrMem = new RendererSharedMemory(this);
@@ -138,7 +144,10 @@
             }
             stats.totalOffHeap = 0L;
         }
-        stroking = 0;
+        stroking   = 0;
+        doClip     = false;
+        closedPath = false;
+
         // if context is maked as DIRTY:
         if (dirty) {
             // may happen if an exception if thrown in the pipeline processing:
@@ -160,7 +169,7 @@
 
         // create a new Path2D ?
         if (p2d == null) {
-            p2d = new Path2D(Path2D.WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
+            p2d = new Path2D(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
 
             // update weak reference:
             refPath2D = new WeakReference<Path2D>(p2d);
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererNoAA.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererNoAA.java	Wed Dec 20 12:22:56 2017 -0800
@@ -37,6 +37,9 @@
 
     private static final double POWER_2_TO_32 = 0x1.0p32d;
 
+    private static final float RDR_OFFSET_X = 0.5f;
+    private static final float RDR_OFFSET_Y = 0.5f;
+
     // common to all types of input path segments.
     // OFFSET as bytes
     // only integer values:
@@ -643,7 +646,7 @@
     }
 
     @Override
-    public void moveTo(float pix_x0, float pix_y0) {
+    public void moveTo(final float pix_x0, final float pix_y0) {
         closePath();
         final float sx = tosubpixx(pix_x0);
         final float sy = tosubpixy(pix_y0);
@@ -654,7 +657,7 @@
     }
 
     @Override
-    public void lineTo(float pix_x1, float pix_y1) {
+    public void lineTo(final float pix_x1, final float pix_y1) {
         final float x1 = tosubpixx(pix_x1);
         final float y1 = tosubpixy(pix_y1);
         addLine(x0, y0, x1, y1);
@@ -663,24 +666,26 @@
     }
 
     @Override
-    public void curveTo(float x1, float y1,
-            float x2, float y2,
-            float x3, float y3)
+    public void curveTo(final float pix_x1, final float pix_y1,
+                        final float pix_x2, final float pix_y2,
+                        final float pix_x3, final float pix_y3)
     {
-        final float xe = tosubpixx(x3);
-        final float ye = tosubpixy(y3);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
-                          tosubpixx(x2), tosubpixy(y2), xe, ye);
+        final float xe = tosubpixx(pix_x3);
+        final float ye = tosubpixy(pix_y3);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+                  tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
         curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
     }
 
     @Override
-    public void quadTo(float x1, float y1, float x2, float y2) {
-        final float xe = tosubpixx(x2);
-        final float ye = tosubpixy(y2);
-        curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+    public void quadTo(final float pix_x1, final float pix_y1,
+                       final float pix_x2, final float pix_y2)
+    {
+        final float xe = tosubpixx(pix_x2);
+        final float ye = tosubpixy(pix_y2);
+        curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
         quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
         x0 = xe;
         y0 = ye;
@@ -688,9 +693,11 @@
 
     @Override
     public void closePath() {
-        addLine(x0, y0, sx0, sy0);
-        x0 = sx0;
-        y0 = sy0;
+        if (x0 != sx0 || y0 != sy0) {
+            addLine(x0, y0, sx0, sy0);
+            x0 = sx0;
+            y0 = sy0;
+        }
     }
 
     @Override
@@ -1402,11 +1409,7 @@
             // ie number of primitives:
 
             // fast check min width:
-            if (width <= RLE_MIN_WIDTH) {
-                useRLE = false;
-            } else {
-                useRLE = true;
-            }
+            useRLE = (width > RLE_MIN_WIDTH);
         }
     }
 
@@ -1483,4 +1486,14 @@
     public int getOutpixMaxY() {
         return bboxY1;
     }
+
+    @Override
+    public float getOffsetX() {
+        return RDR_OFFSET_X;
+    }
+
+    @Override
+    public float getOffsetY() {
+        return RDR_OFFSET_Y;
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererStats.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererStats.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -64,10 +64,6 @@
         = new StatLong("cache.rowAAChunk");
     final StatLong stat_cache_tiles
         = new StatLong("cache.tiles");
-    final StatLong stat_rdr_poly_stack_curves
-        = new StatLong("renderer.poly.stack.curves");
-    final StatLong stat_rdr_poly_stack_types
-        = new StatLong("renderer.poly.stack.types");
     final StatLong stat_rdr_addLine
         = new StatLong("renderer.addLine");
     final StatLong stat_rdr_addLine_skip
@@ -104,15 +100,25 @@
         = new StatLong("renderer.crossings.bsearch");
     final StatLong stat_rdr_crossings_msorts
         = new StatLong("renderer.crossings.msorts");
+    final StatLong stat_str_polystack_curves
+        = new StatLong("stroker.polystack.curves");
+    final StatLong stat_str_polystack_types
+        = new StatLong("stroker.polystack.types");
+    final StatLong stat_cpd_polystack_curves
+        = new StatLong("closedPathDetector.polystack.curves");
+    final StatLong stat_cpd_polystack_types
+        = new StatLong("closedPathDetector.polystack.types");
+    final StatLong stat_pcf_idxstack_indices
+        = new StatLong("pathClipFilter.stack.indices");
     // growable arrays
     final StatLong stat_array_dasher_dasher
         = new StatLong("array.dasher.dasher.d_float");
     final StatLong stat_array_dasher_firstSegmentsBuffer
         = new StatLong("array.dasher.firstSegmentsBuffer.d_float");
-    final StatLong stat_array_stroker_polystack_curves
-        = new StatLong("array.stroker.polystack.curves.d_float");
-    final StatLong stat_array_stroker_polystack_curveTypes
-        = new StatLong("array.stroker.polystack.curveTypes.d_byte");
+    final StatLong stat_array_marlincache_rowAAChunk
+        = new StatLong("array.marlincache.rowAAChunk.resize");
+    final StatLong stat_array_marlincache_touchedTile
+        = new StatLong("array.marlincache.touchedTile.int");
     final StatLong stat_array_renderer_alphaline
         = new StatLong("array.renderer.alphaline.int");
     final StatLong stat_array_renderer_crossings
@@ -127,11 +133,19 @@
         = new StatLong("array.renderer.edgePtrs.int");
     final StatLong stat_array_renderer_aux_edgePtrs
         = new StatLong("array.renderer.aux_edgePtrs.int");
+    final StatLong stat_array_str_polystack_curves
+        = new StatLong("array.stroker.polystack.curves.d_float");
+    final StatLong stat_array_str_polystack_types
+        = new StatLong("array.stroker.polystack.curveTypes.d_byte");
+    final StatLong stat_array_cpd_polystack_curves
+        = new StatLong("array.closedPathDetector.polystack.curves.d_float");
+    final StatLong stat_array_cpd_polystack_types
+        = new StatLong("array.closedPathDetector.polystack.curveTypes.d_byte");
+    final StatLong stat_array_pcf_idxstack_indices
+        = new StatLong("array.pathClipFilter.stack.indices.d_int");
     // histograms
     final Histogram hist_rdr_edges_count
         = new Histogram("renderer.edges.count");
-    final Histogram hist_rdr_poly_stack_curves
-        = new Histogram("renderer.polystack.curves");
     final Histogram hist_rdr_crossings
         = new Histogram("renderer.crossings");
     final Histogram hist_rdr_crossings_ratio
@@ -142,17 +156,27 @@
         = new Histogram("renderer.crossings.msorts");
     final Histogram hist_rdr_crossings_msorts_adds
         = new Histogram("renderer.crossings.msorts.adds");
+    final Histogram hist_str_polystack_curves
+        = new Histogram("stroker.polystack.curves");
+    final Histogram hist_tile_generator_alpha
+        = new Histogram("tile_generator.alpha");
     final Histogram hist_tile_generator_encoding
         = new Histogram("tile_generator.encoding");
     final Histogram hist_tile_generator_encoding_dist
         = new Histogram("tile_generator.encoding.dist");
+    final Histogram hist_tile_generator_encoding_ratio
+        = new Histogram("tile_generator.encoding.ratio");
+    final Histogram hist_tile_generator_encoding_runLen
+        = new Histogram("tile_generator.encoding.runLen");
+    final Histogram hist_cpd_polystack_curves
+        = new Histogram("closedPathDetector.polystack.curves");
+    final Histogram hist_pcf_idxstack_indices
+        = new Histogram("pathClipFilter.stack.indices");
     // all stats
     final StatLong[] statistics = new StatLong[]{
         stat_cache_rowAA,
         stat_cache_rowAAChunk,
         stat_cache_tiles,
-        stat_rdr_poly_stack_types,
-        stat_rdr_poly_stack_curves,
         stat_rdr_addLine,
         stat_rdr_addLine_skip,
         stat_rdr_curveBreak,
@@ -171,26 +195,41 @@
         stat_rdr_crossings_sorts,
         stat_rdr_crossings_bsearch,
         stat_rdr_crossings_msorts,
+        stat_str_polystack_types,
+        stat_str_polystack_curves,
+        stat_cpd_polystack_curves,
+        stat_cpd_polystack_types,
+        stat_pcf_idxstack_indices,
         hist_rdr_edges_count,
-        hist_rdr_poly_stack_curves,
         hist_rdr_crossings,
         hist_rdr_crossings_ratio,
         hist_rdr_crossings_adds,
         hist_rdr_crossings_msorts,
         hist_rdr_crossings_msorts_adds,
+        hist_tile_generator_alpha,
         hist_tile_generator_encoding,
         hist_tile_generator_encoding_dist,
+        hist_tile_generator_encoding_ratio,
+        hist_tile_generator_encoding_runLen,
+        hist_str_polystack_curves,
+        hist_cpd_polystack_curves,
+        hist_pcf_idxstack_indices,
         stat_array_dasher_dasher,
         stat_array_dasher_firstSegmentsBuffer,
-        stat_array_stroker_polystack_curves,
-        stat_array_stroker_polystack_curveTypes,
+        stat_array_marlincache_rowAAChunk,
+        stat_array_marlincache_touchedTile,
         stat_array_renderer_alphaline,
         stat_array_renderer_crossings,
         stat_array_renderer_aux_crossings,
         stat_array_renderer_edgeBuckets,
         stat_array_renderer_edgeBucketCounts,
         stat_array_renderer_edgePtrs,
-        stat_array_renderer_aux_edgePtrs
+        stat_array_renderer_aux_edgePtrs,
+        stat_array_str_polystack_curves,
+        stat_array_str_polystack_types,
+        stat_array_cpd_polystack_curves,
+        stat_array_cpd_polystack_types,
+        stat_array_pcf_idxstack_indices
     };
     // monitors
     final Monitor mon_pre_getAATileGenerator
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Stroker.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Stroker.java	Wed Dec 20 12:22:56 2017 -0800
@@ -28,6 +28,7 @@
 import java.util.Arrays;
 
 import com.sun.javafx.geom.PathConsumer2D;
+import com.sun.marlin.Helpers.PolyStack;
 
 // TODO: some of the arithmetic here is too verbose and prone to hard to
 // debug typos. We should consider making a small Point/Vector class that
@@ -38,42 +39,16 @@
     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
     private static final int CLOSE = 2;
 
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_MITER = 0;
-
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_ROUND = 1;
-
-    /**
-     * Constant value for join style.
-     */
-    public static final int JOIN_BEVEL = 2;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_BUTT = 0;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_ROUND = 1;
-
-    /**
-     * Constant value for end cap style.
-     */
-    public static final int CAP_SQUARE = 2;
-
     // pisces used to use fixed point arithmetic with 16 decimal digits. I
     // didn't want to change the values of the constant below when I converted
     // it to floating point, so that's why the divisions by 2^16 are there.
     private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;
 
-    private static final float C = 0.5522847498307933f;
+    // kappa = (4/3) * (SQRT(2) - 1)
+    private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+
+    // SQRT(2)
+    private static final float SQRT_2 = (float)Math.sqrt(2.0d);
 
     private static final int MAX_N_CURVES = 11;
 
@@ -120,6 +95,20 @@
     // dirty curve
     final Curve curve;
 
+    // Bounds of the drawing region, at pixel precision.
+    private float[] clipRect;
+
+    // the outcode of the current point
+    private int cOutCode = 0;
+
+    // the outcode of the starting point
+    private int sOutCode = 0;
+
+    // flag indicating if the path is opened (clipped)
+    private boolean opened = false;
+    // flag indicating if the starting point's cap is done
+    private boolean capStart = false;
+
     /**
      * Constructs a <code>Stroker</code>.
      * @param rdrCtx per-thread renderer context
@@ -127,7 +116,15 @@
     Stroker(final RendererContext rdrCtx) {
         this.rdrCtx = rdrCtx;
 
-        this.reverse = new PolyStack(rdrCtx);
+        this.reverse = (rdrCtx.stats != null) ?
+            new PolyStack(rdrCtx,
+                    rdrCtx.stats.stat_str_polystack_types,
+                    rdrCtx.stats.stat_str_polystack_curves,
+                    rdrCtx.stats.hist_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_types)
+            : new PolyStack(rdrCtx);
+
         this.curve = rdrCtx.curve;
     }
 
@@ -143,13 +140,19 @@
      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
      * <code>JOIN_BEVEL</code>.
      * @param miterLimit the desired miter limit
+     * @param scale scaling factor applied to clip boundaries
+     * @param rdrOffX renderer's coordinate offset on X axis
+     * @param rdrOffY renderer's coordinate offset on Y axis
      * @return this instance
      */
-    public Stroker init(PathConsumer2D pc2d,
-              float lineWidth,
-              int capStyle,
-              int joinStyle,
-              float miterLimit)
+    public Stroker init(final PathConsumer2D pc2d,
+                        final float lineWidth,
+                        final int capStyle,
+                        final int joinStyle,
+                        final float miterLimit,
+                        final float scale,
+                        double rdrOffX,
+                        double rdrOffY)
     {
         this.out = pc2d;
 
@@ -158,13 +161,44 @@
         this.capStyle = capStyle;
         this.joinStyle = joinStyle;
 
-        float limit = miterLimit * lineWidth2;
+        final float limit = miterLimit * lineWidth2;
         this.miterLimitSq = limit * limit;
 
         this.prev = CLOSE;
 
         rdrCtx.stroking = 1;
 
+        if (rdrCtx.doClip) {
+            // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+            float margin = lineWidth2;
+
+            if (capStyle == CAP_SQUARE) {
+                margin *= SQRT_2;
+            }
+            if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+                margin = limit;
+            }
+            if (scale != 1.0f) {
+                margin  *= scale;
+                rdrOffX *= scale;
+                rdrOffY *= scale;
+            }
+            // add a small rounding error:
+            margin += 1e-3f;
+
+            // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+            // adjust clip rectangle (ymin, ymax, xmin, xmax):
+            final float[] _clipRect = rdrCtx.clipRect;
+            _clipRect[0] -= margin - rdrOffY;
+            _clipRect[1] += margin + rdrOffY;
+            _clipRect[2] -= margin - rdrOffX;
+            _clipRect[3] += margin + rdrOffX;
+            this.clipRect = _clipRect;
+        } else {
+            this.clipRect = null;
+            this.cOutCode = 0;
+            this.sOutCode = 0;
+        }
         return this; // fluent API
     }
 
@@ -175,6 +209,9 @@
     void dispose() {
         reverse.dispose();
 
+        opened   = false;
+        capStart = false;
+
         if (DO_CLEAN_DIRTY) {
             // Force zero-fill dirty arrays:
             Arrays.fill(offset0, 0.0f);
@@ -445,19 +482,62 @@
     }
 
     @Override
-    public void moveTo(float x0, float y0) {
-        if (prev == DRAWING_OP_TO) {
-            finish();
+    public void moveTo(final float x0, final float y0) {
+        moveTo(x0, y0, cOutCode);
+        // update starting point:
+        this.sx0 = x0;
+        this.sy0 = y0;
+        this.sdx = 1.0f;
+        this.sdy = 0.0f;
+        this.opened   = false;
+        this.capStart = false;
+
+        if (clipRect != null) {
+            final int outcode = Helpers.outcode(x0, y0, clipRect);
+            this.cOutCode = outcode;
+            this.sOutCode = outcode;
         }
-        this.sx0 = this.cx0 = x0;
-        this.sy0 = this.cy0 = y0;
-        this.cdx = this.sdx = 1.0f;
-        this.cdy = this.sdy = 0.0f;
-        this.prev = MOVE_TO;
+    }
+
+    private void moveTo(final float x0, final float y0,
+                        final int outcode)
+    {
+        if (prev == MOVE_TO) {
+            this.cx0 = x0;
+            this.cy0 = y0;
+        } else {
+            if (prev == DRAWING_OP_TO) {
+                finish(outcode);
+            }
+            this.prev = MOVE_TO;
+            this.cx0 = x0;
+            this.cy0 = y0;
+            this.cdx = 1.0f;
+            this.cdy = 0.0f;
+        }
     }
 
     @Override
-    public void lineTo(float x1, float y1) {
+    public void lineTo(final float x1, final float y1) {
+        lineTo(x1, y1, false);
+    }
+
+    private void lineTo(final float x1, final float y1,
+                        final boolean force)
+    {
+        final int outcode0 = this.cOutCode;
+        if (!force && clipRect != null) {
+            final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+            this.cOutCode = outcode1;
+
+            // basic rejection criteria
+            if ((outcode0 & outcode1) != 0) {
+                moveTo(x1, y1, outcode0);
+                opened = true;
+                return;
+            }
+        }
+
         float dx = x1 - cx0;
         float dy = y1 - cy0;
         if (dx == 0.0f && dy == 0.0f) {
@@ -467,7 +547,7 @@
         final float mx = offset0[0];
         final float my = offset0[1];
 
-        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
+        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
 
         emitLineTo(cx0 + mx, cy0 + my);
         emitLineTo( x1 + mx,  y1 + my);
@@ -475,43 +555,65 @@
         emitLineToRev(cx0 - mx, cy0 - my);
         emitLineToRev( x1 - mx,  y1 - my);
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = x1;
+        this.cy0 = y1;
+        this.cdx = dx;
+        this.cdy = dy;
         this.cmx = mx;
         this.cmy = my;
-        this.cdx = dx;
-        this.cdy = dy;
-        this.cx0 = x1;
-        this.cy0 = y1;
-        this.prev = DRAWING_OP_TO;
     }
 
     @Override
     public void closePath() {
-        if (prev != DRAWING_OP_TO) {
+        // distinguish empty path at all vs opened path ?
+        if (prev != DRAWING_OP_TO && !opened) {
             if (prev == CLOSE) {
                 return;
             }
             emitMoveTo(cx0, cy0 - lineWidth2);
-            this.cmx = this.smx = 0.0f;
-            this.cmy = this.smy = -lineWidth2;
-            this.cdx = this.sdx = 1.0f;
-            this.cdy = this.sdy = 0.0f;
-            finish();
+
+            this.sdx = 1.0f;
+            this.sdy = 0.0f;
+            this.cdx = 1.0f;
+            this.cdy = 0.0f;
+
+            this.smx = 0.0f;
+            this.smy = -lineWidth2;
+            this.cmx = 0.0f;
+            this.cmy = -lineWidth2;
+
+            finish(cOutCode);
             return;
         }
 
-        if (cx0 != sx0 || cy0 != sy0) {
-            lineTo(sx0, sy0);
+        // basic acceptance criteria
+        if ((sOutCode & cOutCode) == 0) {
+            if (cx0 != sx0 || cy0 != sy0) {
+                lineTo(sx0, sy0, true);
+            }
+
+            drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, sOutCode);
+
+            emitLineTo(sx0 + smx, sy0 + smy);
+
+            if (opened) {
+                emitLineTo(sx0 - smx, sy0 - smy);
+            } else {
+                emitMoveTo(sx0 - smx, sy0 - smy);
+            }
         }
-
-        drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
-
-        emitLineTo(sx0 + smx, sy0 + smy);
-
-        emitMoveTo(sx0 - smx, sy0 - smy);
+        // Ignore caps like finish(false)
         emitReverse();
 
         this.prev = CLOSE;
-        emitClose();
+
+        if (opened) {
+            // do not emit close
+            opened = false;
+        } else {
+            emitClose();
+        }
     }
 
     private void emitReverse() {
@@ -521,7 +623,7 @@
     @Override
     public void pathDone() {
         if (prev == DRAWING_OP_TO) {
-            finish();
+            finish(cOutCode);
         }
 
         out.pathDone();
@@ -534,23 +636,39 @@
         dispose();
     }
 
-    private void finish() {
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(cx0, cy0, cmx, cmy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
-            emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+    private void finish(final int outcode) {
+        // Problem: impossible to guess if the path will be closed in advance
+        //          i.e. if caps must be drawn or not ?
+        // Solution: use the ClosedPathDetector before Stroker to determine
+        // if the path is a closed path or not
+        if (!rdrCtx.closedPath) {
+            if (outcode == 0) {
+                // current point = end's cap:
+                if (capStyle == CAP_ROUND) {
+                    drawRoundCap(cx0, cy0, cmx, cmy);
+                } else if (capStyle == CAP_SQUARE) {
+                    emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
+                    emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+                }
+            }
+            emitReverse();
+
+            if (!capStart) {
+                capStart = true;
+
+                if (sOutCode == 0) {
+                    // starting point = initial cap:
+                    if (capStyle == CAP_ROUND) {
+                        drawRoundCap(sx0, sy0, -smx, -smy);
+                    } else if (capStyle == CAP_SQUARE) {
+                        emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
+                        emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+                    }
+                }
+            }
+        } else {
+            emitReverse();
         }
-
-        emitReverse();
-
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(sx0, sy0, -smx, -smy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
-            emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
-        }
-
         emitClose();
     }
 
@@ -622,23 +740,28 @@
                           float x0, float y0,
                           float dx, float dy,
                           float omx, float omy,
-                          float mx, float my)
+                          float mx, float my,
+                          final int outcode)
     {
         if (prev != DRAWING_OP_TO) {
             emitMoveTo(x0 + mx, y0 + my);
-            this.sdx = dx;
-            this.sdy = dy;
-            this.smx = mx;
-            this.smy = my;
+            if (!opened) {
+                this.sdx = dx;
+                this.sdy = dy;
+                this.smx = mx;
+                this.smy = my;
+            }
         } else {
-            boolean cw = isCW(pdx, pdy, dx, dy);
-            if (joinStyle == JOIN_MITER) {
-                drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
-            } else if (joinStyle == JOIN_ROUND) {
-                drawRoundJoin(x0, y0,
-                              omx, omy,
-                              mx, my, cw,
-                              ROUND_JOIN_THRESHOLD);
+            final boolean cw = isCW(pdx, pdy, dx, dy);
+            if (outcode == 0) {
+                if (joinStyle == JOIN_MITER) {
+                    drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
+                } else if (joinStyle == JOIN_ROUND) {
+                    drawRoundJoin(x0, y0,
+                                  omx, omy,
+                                  mx, my, cw,
+                                  ROUND_JOIN_THRESHOLD);
+                }
             }
             emitLineTo(x0, y0, !cw);
         }
@@ -943,10 +1066,29 @@
         return ret;
     }
 
-    @Override public void curveTo(float x1, float y1,
-                                  float x2, float y2,
-                                  float x3, float y3)
+    @Override
+    public void curveTo(final float x1, final float y1,
+                        final float x2, final float y2,
+                        final float x3, final float y3)
     {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode3 = Helpers.outcode(x3, y3, clipRect);
+            this.cOutCode = outcode3;
+
+            if ((outcode0 & outcode3) != 0) {
+                final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+                final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
+                    moveTo(x3, y3, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final float[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
@@ -955,7 +1097,7 @@
         mid[6] = x3;  mid[7] = y3;
 
         // need these so we can update the state at the end of this method
-        final float xf = mid[6], yf = mid[7];
+        final float xf = x3, yf = y3;
         float dxs = mid[2] - mid[0];
         float dys = mid[3] - mid[1];
         float dxf = mid[6] - mid[4];
@@ -981,6 +1123,10 @@
         }
         if (dxs == 0.0f && dys == 0.0f) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
@@ -999,7 +1145,7 @@
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
 
@@ -1034,16 +1180,36 @@
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = xf;
+        this.cy0 = yf;
+        this.cdx = dxf;
+        this.cdy = dyf;
         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cx0 = xf;
-        this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
     }
 
-    @Override public void quadTo(float x1, float y1, float x2, float y2) {
+    @Override
+    public void quadTo(final float x1, final float y1,
+                       final float x2, final float y2)
+    {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+            this.cOutCode = outcode2;
+
+            if ((outcode0 & outcode2) != 0) {
+                final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2) != 0) {
+                    moveTo(x2, y2, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final float[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
@@ -1051,7 +1217,7 @@
         mid[4] = x2;  mid[5] = y2;
 
         // need these so we can update the state at the end of this method
-        final float xf = mid[4], yf = mid[5];
+        final float xf = x2, yf = y2;
         float dxs = mid[2] - mid[0];
         float dys = mid[3] - mid[1];
         float dxf = mid[4] - mid[2];
@@ -1062,6 +1228,10 @@
         }
         if (dxs == 0.0f && dys == 0.0f) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
@@ -1079,7 +1249,7 @@
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
 
@@ -1114,210 +1284,13 @@
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
+        this.prev = DRAWING_OP_TO;
+        this.cx0 = xf;
+        this.cy0 = yf;
+        this.cdx = dxf;
+        this.cdy = dyf;
         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cx0 = xf;
-        this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
     }
 
-    // a stack of polynomial curves where each curve shares endpoints with
-    // adjacent ones.
-    static final class PolyStack {
-        private static final byte TYPE_LINETO  = (byte) 0;
-        private static final byte TYPE_QUADTO  = (byte) 1;
-        private static final byte TYPE_CUBICTO = (byte) 2;
-
-        // curves capacity = edges count (8192) = edges x 2 (coords)
-        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
-        // types capacity = edges count (4096)
-        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
-        float[] curves;
-        int end;
-        byte[] curveTypes;
-        int numCurves;
-
-        // per-thread renderer context
-        final RendererContext rdrCtx;
-
-        // curves ref (dirty)
-        final FloatArrayCache.Reference curves_ref;
-        // curveTypes ref (dirty)
-        final ByteArrayCache.Reference curveTypes_ref;
-
-        // used marks (stats only)
-        int curveTypesUseMark;
-        int curvesUseMark;
-
-        /**
-         * Constructor
-         * @param rdrCtx per-thread renderer context
-         */
-        PolyStack(final RendererContext rdrCtx) {
-            this.rdrCtx = rdrCtx;
-
-            curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
-            curves     = curves_ref.initial;
-
-            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
-            curveTypes     = curveTypes_ref.initial;
-            numCurves = 0;
-            end = 0;
-
-            if (DO_STATS) {
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-        }
-
-        /**
-         * Disposes this PolyStack:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            end = 0;
-            numCurves = 0;
-
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);
-                rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark);
-                rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark);
-
-                // reset marks
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-
-            // Return arrays:
-            // curves and curveTypes are kept dirty
-            curves     = curves_ref.putArray(curves);
-            curveTypes = curveTypes_ref.putArray(curveTypes);
-        }
-
-        private void ensureSpace(final int n) {
-            // use substraction to avoid integer overflow:
-            if (curves.length - end < n) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curves
-                        .add(end + n);
-                }
-                curves = curves_ref.widenArray(curves, end, end + n);
-            }
-            if (curveTypes.length <= numCurves) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curveTypes
-                        .add(numCurves + 1);
-                }
-                curveTypes = curveTypes_ref.widenArray(curveTypes,
-                                                       numCurves,
-                                                       numCurves + 1);
-            }
-        }
-
-        void pushCubic(float x0, float y0,
-                       float x1, float y1,
-                       float x2, float y2)
-        {
-            ensureSpace(6);
-            curveTypes[numCurves++] = TYPE_CUBICTO;
-            // we reverse the coordinate order to make popping easier
-            final float[] _curves = curves;
-            int e = end;
-            _curves[e++] = x2;    _curves[e++] = y2;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushQuad(float x0, float y0,
-                      float x1, float y1)
-        {
-            ensureSpace(4);
-            curveTypes[numCurves++] = TYPE_QUADTO;
-            final float[] _curves = curves;
-            int e = end;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushLine(float x, float y) {
-            ensureSpace(2);
-            curveTypes[numCurves++] = TYPE_LINETO;
-            curves[end++] = x;    curves[end++] = y;
-        }
-
-        void popAll(PathConsumer2D io) {
-            if (DO_STATS) {
-                // update used marks:
-                if (numCurves > curveTypesUseMark) {
-                    curveTypesUseMark = numCurves;
-                }
-                if (end > curvesUseMark) {
-                    curvesUseMark = end;
-                }
-            }
-            final byte[]  _curveTypes = curveTypes;
-            final float[] _curves = curves;
-            int nc = numCurves;
-            int e  = end;
-
-            while (nc != 0) {
-                switch(_curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    e -= 2;
-                    io.lineTo(_curves[e], _curves[e+1]);
-                    continue;
-                case TYPE_QUADTO:
-                    e -= 4;
-                    io.quadTo(_curves[e+0], _curves[e+1],
-                              _curves[e+2], _curves[e+3]);
-                    continue;
-                case TYPE_CUBICTO:
-                    e -= 6;
-                    io.curveTo(_curves[e+0], _curves[e+1],
-                               _curves[e+2], _curves[e+3],
-                               _curves[e+4], _curves[e+5]);
-                    continue;
-                default:
-                }
-            }
-            numCurves = 0;
-            end = 0;
-        }
-
-        @Override
-        public String toString() {
-            String ret = "";
-            int nc = numCurves;
-            int last = end;
-            int len;
-            while (nc != 0) {
-                switch(curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    len = 2;
-                    ret += "line: ";
-                    break;
-                case TYPE_QUADTO:
-                    len = 4;
-                    ret += "quad: ";
-                    break;
-                case TYPE_CUBICTO:
-                    len = 6;
-                    ret += "cubic: ";
-                    break;
-                default:
-                    len = 0;
-                }
-                last -= len;
-                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
-                                       + "\n";
-            }
-            return ret;
-        }
-    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/TransformingPathConsumer2D.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/TransformingPathConsumer2D.java	Wed Dec 20 12:22:56 2017 -0800
@@ -28,49 +28,175 @@
 import com.sun.javafx.geom.Path2D;
 import com.sun.javafx.geom.PathConsumer2D;
 import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.marlin.Helpers.IndexStack;
+import com.sun.marlin.Helpers.PolyStack;
 
 public final class TransformingPathConsumer2D {
 
-    TransformingPathConsumer2D() {
-        // used by RendererContext
-    }
+    private final RendererContext rdrCtx;
 
-    // recycled PathConsumer2D instance from wrapPath2d()
+    // recycled ClosedPathDetector instance from detectClosedPath()
+    private final ClosedPathDetector   cpDetector;
+
+    // recycled PathClipFilter instance from pathClipper()
+    private final PathClipFilter       pathClipper;
+
+    // recycled PathConsumer2D instance from wrapPath2D()
     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
 
     // recycled PathConsumer2D instances from deltaTransformConsumer()
     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
 
+    // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
+    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
+    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+
+    // recycled PathTracer instances from tracer...() methods
+    private final PathTracer tracerInput      = new PathTracer("[Input]");
+    private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
+    private final PathTracer tracerFiller     = new PathTracer("Filler");
+    private final PathTracer tracerStroker    = new PathTracer("Stroker");
+
+    TransformingPathConsumer2D(final RendererContext rdrCtx) {
+        // used by RendererContext
+        this.rdrCtx = rdrCtx;
+        this.cpDetector = new ClosedPathDetector(rdrCtx);
+        this.pathClipper = new PathClipFilter(rdrCtx);
+    }
+
     public PathConsumer2D wrapPath2D(Path2D p2d) {
         return wp_Path2DWrapper.init(p2d);
     }
 
+    public PathConsumer2D traceInput(PathConsumer2D out) {
+        return tracerInput.init(out);
+    }
+
+    public PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
+        return tracerCPDetector.init(out);
+    }
+
+    public PathConsumer2D traceFiller(PathConsumer2D out) {
+        return tracerFiller.init(out);
+    }
+
+    public PathConsumer2D traceStroker(PathConsumer2D out) {
+        return tracerStroker.init(out);
+    }
+
+    public PathConsumer2D detectClosedPath(PathConsumer2D out) {
+        return cpDetector.init(out);
+    }
+
+    public PathConsumer2D pathClipper(PathConsumer2D out,
+                                      final float rdrOffX,
+                                      final float rdrOffY)
+    {
+        return pathClipper.init(out, rdrOffX, rdrOffY);
+    }
+
     public PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
-                                                 BaseTransform at)
+                                                 BaseTransform at,
+                                                 final float rdrOffX,
+                                                 final float rdrOffY)
     {
         if (at == null) {
             return out;
         }
-        float mxx = (float) at.getMxx();
-        float mxy = (float) at.getMxy();
-        float myx = (float) at.getMyx();
-        float myy = (float) at.getMyy();
+        final float mxx = (float) at.getMxx();
+        final float mxy = (float) at.getMxy();
+        final float myx = (float) at.getMyx();
+        final float myy = (float) at.getMyy();
 
         if (mxy == 0.0f && myx == 0.0f) {
             if (mxx == 1.0f && myy == 1.0f) {
                 return out;
             } else {
+                // Scale only
+                if (rdrCtx.doClip) {
+                    // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                    adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                    adjustClipScale(rdrCtx.clipRect, mxx, myy);
+                }
                 return dt_DeltaScaleFilter.init(out, mxx, myy);
             }
         } else {
+            if (rdrCtx.doClip) {
+                // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
+            }
             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
         }
     }
 
-    // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
-    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
-    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+    private static void adjustClipOffset(final float[] clipRect,
+                                         final float rdrOffX,
+                                         final float rdrOffY)
+    {
+        clipRect[0] += rdrOffY;
+        clipRect[1] += rdrOffY;
+        clipRect[2] += rdrOffX;
+        clipRect[3] += rdrOffX;
+    }
+
+    private static void adjustClipScale(final float[] clipRect,
+                                        final float mxx, final float myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaScaleFilter):
+        clipRect[0] /= myy;
+        clipRect[1] /= myy;
+        clipRect[2] /= mxx;
+        clipRect[3] /= mxx;
+    }
+
+    private static void adjustClipInverseDelta(final float[] clipRect,
+                                               final float mxx, final float mxy,
+                                               final float myx, final float myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaTransformFilter):
+        final float det = mxx * myy - mxy * myx;
+        final float imxx =  myy / det;
+        final float imxy = -mxy / det;
+        final float imyx = -myx / det;
+        final float imyy =  mxx / det;
+
+        float xmin, xmax, ymin, ymax;
+        float x, y;
+        // xmin, ymin:
+        x = clipRect[2] * imxx + clipRect[0] * imxy;
+        y = clipRect[2] * imyx + clipRect[0] * imyy;
+
+        xmin = xmax = x;
+        ymin = ymax = y;
+
+        // xmax, ymin:
+        x = clipRect[3] * imxx + clipRect[0] * imxy;
+        y = clipRect[3] * imyx + clipRect[0] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmin, ymax:
+        x = clipRect[2] * imxx + clipRect[1] * imxy;
+        y = clipRect[2] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmax, ymax:
+        x = clipRect[3] * imxx + clipRect[1] * imxy;
+        y = clipRect[3] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        clipRect[0] = ymin;
+        clipRect[1] = ymax;
+        clipRect[2] = xmin;
+        clipRect[3] = xmax;
+    }
 
     public PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
                                                         BaseTransform at)
@@ -90,7 +216,7 @@
                 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
             }
         } else {
-            float det = mxx * myy - mxy * myx;
+            final float det = mxx * myy - mxy * myx;
             return iv_DeltaTransformFilter.init(out,
                                                 myy / det,
                                                -mxy / det,
@@ -258,4 +384,412 @@
             p2d.quadTo(x1, y1, x2, y2);
         }
     }
+
+    static final class ClosedPathDetector implements PathConsumer2D {
+
+        private final RendererContext rdrCtx;
+        private final PolyStack stack;
+
+        private PathConsumer2D out;
+
+        ClosedPathDetector(final RendererContext rdrCtx) {
+            this.rdrCtx = rdrCtx;
+            this.stack = (rdrCtx.stats != null) ?
+                new PolyStack(rdrCtx,
+                        rdrCtx.stats.stat_cpd_polystack_types,
+                        rdrCtx.stats.stat_cpd_polystack_curves,
+                        rdrCtx.stats.hist_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_types)
+                : new PolyStack(rdrCtx);
+        }
+
+        ClosedPathDetector init(PathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        /**
+         * Disposes this instance:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            stack.dispose();
+        }
+
+        @Override
+        public void pathDone() {
+            // previous path is not closed:
+            finish(false);
+            out.pathDone();
+
+            // TODO: fix possible leak if exception happened
+            // Dispose this instance:
+            dispose();
+        }
+
+        @Override
+        public void closePath() {
+            // path is closed
+            finish(true);
+            out.closePath();
+        }
+
+        @Override
+        public void moveTo(float x0, float y0) {
+            // previous path is not closed:
+            finish(false);
+            out.moveTo(x0, y0);
+        }
+
+        private void finish(final boolean closed) {
+            rdrCtx.closedPath = closed;
+            stack.pullAll(out);
+        }
+
+        @Override
+        public void lineTo(float x1, float y1) {
+            stack.pushLine(x1, y1);
+        }
+
+        @Override
+        public void curveTo(float x3, float y3,
+                            float x2, float y2,
+                            float x1, float y1)
+        {
+            stack.pushCubic(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(float x2, float y2, float x1, float y1) {
+            stack.pushQuad(x1, y1, x2, y2);
+        }
+    }
+
+    static final class PathClipFilter implements PathConsumer2D {
+
+        private PathConsumer2D out;
+
+        // Bounds of the drawing region, at pixel precision.
+        private final float[] clipRect;
+
+        private final float[] corners = new float[8];
+        private boolean init_corners = false;
+
+        private final IndexStack stack;
+
+        // the current outcode of the current sub path
+        private int cOutCode = 0;
+
+        // the cumulated (and) outcode of the complete path
+        private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
+
+        private boolean outside = false;
+
+        // The current point OUTSIDE
+        private float cx0, cy0;
+
+        PathClipFilter(final RendererContext rdrCtx) {
+            this.clipRect = rdrCtx.clipRect;
+            this.stack = (rdrCtx.stats != null) ?
+                new IndexStack(rdrCtx,
+                        rdrCtx.stats.stat_pcf_idxstack_indices,
+                        rdrCtx.stats.hist_pcf_idxstack_indices,
+                        rdrCtx.stats.stat_array_pcf_idxstack_indices)
+                : new IndexStack(rdrCtx);
+        }
+
+        PathClipFilter init(final PathConsumer2D out,
+                            final double rdrOffX,
+                            final double rdrOffY)
+        {
+            this.out = out;
+
+            // add a small rounding error:
+            final float margin = 1e-3f;
+
+            final float[] _clipRect = this.clipRect;
+            // Adjust the clipping rectangle with the renderer offsets
+            _clipRect[0] -= margin - rdrOffY;
+            _clipRect[1] += margin + rdrOffY;
+            _clipRect[2] -= margin - rdrOffX;
+            _clipRect[3] += margin + rdrOffX;
+
+            this.init_corners = true;
+            this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
+
+            return this; // fluent API
+        }
+
+        /**
+         * Disposes this instance:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            stack.dispose();
+        }
+
+        private void finishPath() {
+            if (outside) {
+                // criteria: inside or totally outside ?
+                if (gOutCode == 0) {
+                    finish();
+                } else {
+                    this.outside = false;
+                    stack.reset();
+                }
+            }
+        }
+
+        private void finish() {
+            this.outside = false;
+
+            if (!stack.isEmpty()) {
+                if (init_corners) {
+                    init_corners = false;
+
+                    final float[] _corners = corners;
+                    final float[] _clipRect = clipRect;
+                    // Top Left (0):
+                    _corners[0] = _clipRect[2];
+                    _corners[1] = _clipRect[0];
+                    // Bottom Left (1):
+                    _corners[2] = _clipRect[2];
+                    _corners[3] = _clipRect[1];
+                    // Top right (2):
+                    _corners[4] = _clipRect[3];
+                    _corners[5] = _clipRect[0];
+                    // Bottom Right (3):
+                    _corners[6] = _clipRect[3];
+                    _corners[7] = _clipRect[1];
+                }
+                stack.pullAll(corners, out);
+            }
+            out.lineTo(cx0, cy0);
+        }
+
+        @Override
+        public void pathDone() {
+            finishPath();
+
+            out.pathDone();
+
+            // TODO: fix possible leak if exception happened
+            // Dispose this instance:
+            dispose();
+        }
+
+        @Override
+        public void closePath() {
+            finishPath();
+
+            out.closePath();
+        }
+
+        @Override
+        public void moveTo(final float x0, final float y0) {
+            finishPath();
+
+            final int outcode = Helpers.outcode(x0, y0, clipRect);
+            this.cOutCode = outcode;
+            this.outside = false;
+            out.moveTo(x0, y0);
+        }
+
+        @Override
+        public void lineTo(final float xe, final float ye) {
+            final int outcode0 = this.cOutCode;
+            final int outcode1 = Helpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode1;
+
+            final int sideCode = (outcode0 & outcode1);
+
+            // basic rejection criteria:
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                this.gOutCode &= sideCode;
+                // keep last point coordinate before entering the clip again:
+                this.outside = true;
+                this.cx0 = xe;
+                this.cy0 = ye;
+
+                clip(sideCode, outcode0, outcode1);
+                return;
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.lineTo(xe, ye);
+        }
+
+        private void clip(final int sideCode,
+                          final int outcode0,
+                          final int outcode1)
+        {
+            // corner or cross-boundary on left or right side:
+            if ((outcode0 != outcode1)
+                    && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0))
+            {
+                // combine outcodes:
+                final int mergeCode = (outcode0 | outcode1);
+                final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B;
+                final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R;
+                final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2;
+
+                // add corners to outside stack:
+                switch (tbCode) {
+                    case MarlinConst.OUTCODE_TOP:
+// System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                        stack.push(off); // top
+                        return;
+                    case MarlinConst.OUTCODE_BOTTOM:
+// System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                        stack.push(off + 1); // bottom
+                        return;
+                    default:
+                        // both TOP / BOTTOM:
+                        if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
+// System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                            // top to bottom
+                            stack.push(off); // top
+                            stack.push(off + 1); // bottom
+                        } else {
+// System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
+                            // bottom to top
+                            stack.push(off + 1); // bottom
+                            stack.push(off); // top
+                        }
+                }
+            }
+        }
+
+        @Override
+        public void curveTo(final float x1, final float y1,
+                            final float x2, final float y2,
+                            final float xe, final float ye)
+        {
+            final int outcode0 = this.cOutCode;
+            final int outcode3 = Helpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode3;
+
+            int sideCode = outcode0 & outcode3;
+
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                sideCode &= Helpers.outcode(x1, y1, clipRect);
+                sideCode &= Helpers.outcode(x2, y2, clipRect);
+                this.gOutCode &= sideCode;
+
+                // basic rejection criteria:
+                if (sideCode != 0) {
+                    // keep last point coordinate before entering the clip again:
+                    this.outside = true;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
+
+                    clip(sideCode, outcode0, outcode3);
+                    return;
+                }
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.curveTo(x1, y1, x2, y2, xe, ye);
+        }
+
+        @Override
+        public void quadTo(final float x1, final float y1,
+                           final float xe, final float ye)
+        {
+            final int outcode0 = this.cOutCode;
+            final int outcode2 = Helpers.outcode(xe, ye, clipRect);
+            this.cOutCode = outcode2;
+
+            int sideCode = outcode0 & outcode2;
+
+            if (sideCode == 0) {
+                this.gOutCode = 0;
+            } else {
+                sideCode &= Helpers.outcode(x1, y1, clipRect);
+                this.gOutCode &= sideCode;
+
+                // basic rejection criteria:
+                if (sideCode != 0) {
+                    // keep last point coordinate before entering the clip again:
+                    this.outside = true;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
+
+                    clip(sideCode, outcode0, outcode2);
+                    return;
+                }
+            }
+            if (outside) {
+                finish();
+            }
+            // clipping disabled:
+            out.quadTo(x1, y1, xe, ye);
+        }
+    }
+
+    static final class PathTracer implements PathConsumer2D {
+        private final String prefix;
+        private PathConsumer2D out;
+
+        PathTracer(String name) {
+            this.prefix = name + ": ";
+        }
+
+        PathTracer init(PathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        @Override
+        public void moveTo(float x0, float y0) {
+            log("moveTo (" + x0 + ", " + y0 + ')');
+            out.moveTo(x0, y0);
+        }
+
+        @Override
+        public void lineTo(float x1, float y1) {
+            log("lineTo (" + x1 + ", " + y1 + ')');
+            out.lineTo(x1, y1);
+        }
+
+        @Override
+        public void curveTo(float x1, float y1,
+                            float x2, float y2,
+                            float x3, float y3)
+        {
+            log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
+            out.curveTo(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(float x1, float y1, float x2, float y2) {
+            log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
+            out.quadTo(x1, y1, x2, y2);
+        }
+
+        @Override
+        public void closePath() {
+            log("closePath");
+            out.closePath();
+        }
+
+        @Override
+        public void pathDone() {
+            log("pathDone");
+            out.pathDone();
+        }
+
+        private void log(final String message) {
+            System.out.println(prefix + message);
+        }
+    }
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/marlin/Version.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/marlin/Version.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@
 
 public final class Version {
 
-    private static final String VERSION = "marlinFX-0.7.5-Unsafe-OpenJDK";
+    private static final String VERSION = "marlinFX-0.8.2-Unsafe-OpenJDK";
 
     public static String getVersion() {
         return VERSION;
--- a/modules/javafx.graphics/src/main/java/com/sun/pisces/PiscesRenderer.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/pisces/PiscesRenderer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/BasicStroke.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/BasicStroke.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/es2/MonocleGLContext.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/es2/MonocleGLContext.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/PrismSettings.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/PrismSettings.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderGraphics.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/ps/BaseShaderGraphics.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinPrismUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinPrismUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -32,9 +32,11 @@
 import com.sun.javafx.geom.Shape;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.marlin.MarlinConst;
+import com.sun.marlin.MarlinProperties;
 import com.sun.marlin.DMarlinRenderer;
 import com.sun.marlin.DPathConsumer2D;
 import com.sun.marlin.DRendererContext;
+import com.sun.marlin.DStroker;
 import com.sun.marlin.DTransformingPathConsumer2D;
 import com.sun.prism.BasicStroke;
 
@@ -45,6 +47,13 @@
     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
     static final float LOWER_BND = -UPPER_BND;
 
+    static final boolean DO_CLIP = MarlinProperties.isDoClip();
+    static final boolean DO_CLIP_FILL = true;
+
+    static final boolean DO_TRACE_PATH = false;
+
+    static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
+
     /**
      * Private constructor to prevent instantiation.
      */
@@ -55,7 +64,7 @@
         return Math.abs(num) < 2.0d * Math.ulp(num);
     }
 
-    private static DPathConsumer2D initPipeline(
+    private static DPathConsumer2D initStroker(
             final DRendererContext rdrCtx,
             final BasicStroke stroke,
             final float lineWidth,
@@ -80,89 +89,112 @@
         int dashLen = -1;
         boolean recycleDashes = false;
         double scale = 1.0d;
-        double width = 0.0f, dashphase = 0.0f;
+        double width = lineWidth;
+        float[] dashes = stroke.getDashArray();
         double[] dashesD = null;
+        double dashphase = stroke.getDashPhase();
 
-        if (stroke != null) {
-            width = lineWidth;
-            final float[] dashes = stroke.getDashArray();
-            dashphase = stroke.getDashPhase();
+        // Ensure converting dashes to double precision:
+        if (dashes != null) {
+            recycleDashes = true;
+            dashLen = dashes.length;
+            dashesD = rdrCtx.dasher.copyDashArray(dashes);
+        }
 
-            // Ensure converting dashes to double precision:
-            if (dashes != null) {
-                recycleDashes = true;
-                dashLen = dashes.length;
-                dashesD = rdrCtx.dasher.copyDashArray(dashes);
+        if ((tx != null) && !tx.isIdentity()) {
+            final double a = tx.getMxx();
+            final double b = tx.getMxy();
+            final double c = tx.getMyx();
+            final double d = tx.getMyy();
+
+            // If the transform is a constant multiple of an orthogonal transformation
+            // then every length is just multiplied by a constant, so we just
+            // need to transform input paths to stroker and tell stroker
+            // the scaled width. This condition is satisfied if
+            // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
+            // leave a bit of room for error.
+            if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
+                scale = Math.sqrt(a*a + c*c);
+
+                if (dashesD != null) {
+                    for (int i = 0; i < dashLen; i++) {
+                        dashesD[i] *= scale;
+                    }
+                    dashphase *= scale;
+                }
+                width *= scale;
+
+                // by now strokerat == null. Input paths to
+                // stroker (and maybe dasher) will have the full transform tx
+                // applied to them and nothing will happen to the output paths.
+            } else {
+                strokerTx = tx;
+
+                // by now strokerat == tx. Input paths to
+                // stroker (and maybe dasher) will have the full transform tx
+                // applied to them, then they will be normalized, and then
+                // the inverse of *only the non translation part of tx* will
+                // be applied to the normalized paths. This won't cause problems
+                // in stroker, because, suppose tx = T*A, where T is just the
+                // translation part of tx, and A is the rest. T*A has already
+                // been applied to Stroker/Dasher's input. Then Ainv will be
+                // applied. Ainv*T*A is not equal to T, but it is a translation,
+                // which means that none of stroker's assumptions about its
+                // input will be violated. After all this, A will be applied
+                // to stroker's output.
             }
+        }
 
-            if ((tx != null) && !tx.isIdentity()) {
-                final double a = tx.getMxx();
-                final double b = tx.getMxy();
-                final double c = tx.getMyx();
-                final double d = tx.getMyy();
+        // Get renderer offsets:
+        double rdrOffX = 0.0d, rdrOffY = 0.0d;
 
-                // If the transform is a constant multiple of an orthogonal transformation
-                // then every length is just multiplied by a constant, so we just
-                // need to transform input paths to stroker and tell stroker
-                // the scaled width. This condition is satisfied if
-                // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
-                // leave a bit of room for error.
-                if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
-                    scale = Math.sqrt(a*a + c*c);
-
-                    if (dashesD != null) {
-                        for (int i = 0; i < dashLen; i++) {
-                            dashesD[i] *= scale;
-                        }
-                        dashphase *= scale;
-                    }
-                    width *= scale;
-
-                    // by now strokerat == null. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them and nothing will happen to the output paths.
-                } else {
-                    strokerTx = tx;
-
-                    // by now strokerat == tx. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them, then they will be normalized, and then
-                    // the inverse of *only the non translation part of tx* will
-                    // be applied to the normalized paths. This won't cause problems
-                    // in stroker, because, suppose tx = T*A, where T is just the
-                    // translation part of tx, and A is the rest. T*A has already
-                    // been applied to Stroker/Dasher's input. Then Ainv will be
-                    // applied. Ainv*T*A is not equal to T, but it is a translation,
-                    // which means that none of stroker's assumptions about its
-                    // input will be violated. After all this, A will be applied
-                    // to stroker's output.
-                }
-            }
+        if (rdrCtx.doClip && (tx != null)) {
+            final DMarlinRenderer renderer = (DMarlinRenderer)out;
+            rdrOffX = renderer.getOffsetX();
+            rdrOffY = renderer.getOffsetY();
         }
 
         // Prepare the pipeline:
         DPathConsumer2D pc = out;
 
+        final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+        if (DO_TRACE_PATH) {
+            // trace Stroker:
+            pc = transformerPC2D.traceStroker(pc);
+        }
+
         if (MarlinConst.USE_SIMPLIFIER) {
             // Use simplifier after stroker before Renderer
             // to remove collinear segments (notably due to cap square)
             pc = rdrCtx.simplifier.init(pc);
         }
 
-        final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+        // deltaTransformConsumer may adjust the clip rectangle:
+        pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx, rdrOffX, rdrOffY);
 
-        if (stroke != null) {
-            pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
+        // stroker will adjust the clip rectangle (width / miter limit):
+        pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
+                stroke.getLineJoin(), stroke.getMiterLimit(),
+                scale, rdrOffX, rdrOffY);
 
-            pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
-                    stroke.getLineJoin(), stroke.getMiterLimit());
+        if (dashesD != null) {
+            pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase, recycleDashes);
+        } else if (rdrCtx.doClip && (stroke.getEndCap() != DStroker.CAP_BUTT)) {
+            if (DO_TRACE_PATH) {
+                pc = transformerPC2D.traceClosedPathDetector(pc);
+            }
 
-            if (dashesD != null) {
-                pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase, recycleDashes);
-            }
-            pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
+            // If no dash and clip is enabled:
+            // detect closedPaths (polygons) for caps
+            pc = transformerPC2D.detectClosedPath(pc);
         }
+        pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
 
+        if (DO_TRACE_PATH) {
+            // trace Input:
+            pc = transformerPC2D.traceInput(pc);
+        }
         /*
          * Pipeline seems to be:
          * shape.getPathIterator(tx)
@@ -186,18 +218,52 @@
             final int piRule,
             final DMarlinRenderer renderer)
     {
-        final int oprule = ((stroke == null) && (piRule == PathIterator.WIND_EVEN_ODD)) ?
-            DMarlinRenderer.WIND_EVEN_ODD : DMarlinRenderer.WIND_NON_ZERO;
+        if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
+            // Define the initial clip bounds:
+            final double[] clipRect = rdrCtx.clipRect;
 
-        renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
+            clipRect[0] = clip.y;
+            clipRect[1] = clip.y + clip.height;
+            clipRect[2] = clip.x;
+            clipRect[3] = clip.x + clip.width;
 
-        float lw = 0.0f;
+            // Enable clipping:
+            rdrCtx.doClip = true;
+        }
 
         if (stroke != null) {
-            lw = stroke.getLineWidth();
+            renderer.init(clip.x, clip.y, clip.width, clip.height,
+                          MarlinConst.WIND_NON_ZERO);
+
+            return initStroker(rdrCtx, stroke, stroke.getLineWidth(), tx, renderer);
+        } else {
+            // Filler:
+            final int oprule = (piRule == PathIterator.WIND_EVEN_ODD) ?
+                MarlinConst.WIND_EVEN_ODD : MarlinConst.WIND_NON_ZERO;
+
+            renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
+
+            DPathConsumer2D pc = renderer;
+
+            final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+            if (DO_CLIP_FILL && rdrCtx.doClip) {
+                double rdrOffX = renderer.getOffsetX();
+                double rdrOffY = renderer.getOffsetY();
+
+                if (DO_TRACE_PATH) {
+                    // trace Filler:
+                    pc = rdrCtx.transformerPC2D.traceFiller(pc);
+                }
+                pc = rdrCtx.transformerPC2D.pathClipper(pc, rdrOffX, rdrOffY);
+            }
+
+            if (DO_TRACE_PATH) {
+                // trace Input:
+                pc = transformerPC2D.traceInput(pc);
+            }
+            return pc;
         }
-
-        return initPipeline(rdrCtx, stroke, lw, tx, renderer);
     }
 
     public static DMarlinRenderer setupRenderer(
@@ -233,7 +299,7 @@
             final float lineWidth,
             final DPathConsumer2D out)
     {
-        final DPathConsumer2D pc2d = initPipeline(rdrCtx, stroke, lineWidth, null, out);
+        final DPathConsumer2D pc2d = initStroker(rdrCtx, stroke, lineWidth, null, out);
 
         if (shape instanceof Path2D) {
             feedConsumer(rdrCtx, (Path2D)shape, null, pc2d);
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -33,8 +33,10 @@
 import com.sun.javafx.geom.Shape;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.marlin.MarlinConst;
+import com.sun.marlin.MarlinProperties;
 import com.sun.marlin.MarlinRenderer;
 import com.sun.marlin.RendererContext;
+import com.sun.marlin.Stroker;
 import com.sun.marlin.TransformingPathConsumer2D;
 import com.sun.prism.BasicStroke;
 
@@ -45,6 +47,13 @@
     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
     static final float LOWER_BND = -UPPER_BND;
 
+    static final boolean DO_CLIP = MarlinProperties.isDoClip();
+    static final boolean DO_CLIP_FILL = true;
+
+    static final boolean DO_TRACE_PATH = false;
+
+    static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
+
     /**
      * Private constructor to prevent instantiation.
      */
@@ -55,7 +64,7 @@
         return Math.abs(num) < 2.0d * Math.ulp(num);
     }
 
-    private static PathConsumer2D initPipeline(
+    private static PathConsumer2D initStroker(
             final RendererContext rdrCtx,
             final BasicStroke stroke,
             final float lineWidth,
@@ -80,88 +89,110 @@
         int dashLen = -1;
         boolean recycleDashes = false;
         float scale = 1.0f;
-        float width = 0.0f, dashphase = 0.0f;
-        float[] dashes = null;
+        float width = lineWidth;
+        float[] dashes = stroke.getDashArray();
+        float dashphase = stroke.getDashPhase();
 
-        if (stroke != null) {
-            width = lineWidth;
-            dashes = stroke.getDashArray();
-            dashphase = stroke.getDashPhase();
+        if ((tx != null) && !tx.isIdentity()) {
+            final double a = tx.getMxx();
+            final double b = tx.getMxy();
+            final double c = tx.getMyx();
+            final double d = tx.getMyy();
 
-            if ((tx != null) && !tx.isIdentity()) {
-                final double a = tx.getMxx();
-                final double b = tx.getMxy();
-                final double c = tx.getMyx();
-                final double d = tx.getMyy();
+            // If the transform is a constant multiple of an orthogonal transformation
+            // then every length is just multiplied by a constant, so we just
+            // need to transform input paths to stroker and tell stroker
+            // the scaled width. This condition is satisfied if
+            // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
+            // leave a bit of room for error.
+            if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
+                scale = (float) Math.sqrt(a*a + c*c);
 
-                // If the transform is a constant multiple of an orthogonal transformation
-                // then every length is just multiplied by a constant, so we just
-                // need to transform input paths to stroker and tell stroker
-                // the scaled width. This condition is satisfied if
-                // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
-                // leave a bit of room for error.
-                if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
-                    scale = (float) Math.sqrt(a*a + c*c);
+                if (dashes != null) {
+                    recycleDashes = true;
+                    dashLen = dashes.length;
+                    dashes = rdrCtx.dasher.copyDashArray(dashes);
+                    for (int i = 0; i < dashLen; i++) {
+                        dashes[i] *= scale;
+                    }
+                    dashphase *= scale;
+                }
+                width *= scale;
 
-                    if (dashes != null) {
-                        recycleDashes = true;
-                        dashLen = dashes.length;
-                        dashes = rdrCtx.dasher.copyDashArray(dashes);
-                        for (int i = 0; i < dashLen; i++) {
-                            dashes[i] *= scale;
-                        }
-                        dashphase *= scale;
-                    }
-                    width *= scale;
+                // by now strokerat == null. Input paths to
+                // stroker (and maybe dasher) will have the full transform tx
+                // applied to them and nothing will happen to the output paths.
+            } else {
+                strokerTx = tx;
 
-                    // by now strokerat == null. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them and nothing will happen to the output paths.
-                } else {
-                    strokerTx = tx;
+                // by now strokerat == tx. Input paths to
+                // stroker (and maybe dasher) will have the full transform tx
+                // applied to them, then they will be normalized, and then
+                // the inverse of *only the non translation part of tx* will
+                // be applied to the normalized paths. This won't cause problems
+                // in stroker, because, suppose tx = T*A, where T is just the
+                // translation part of tx, and A is the rest. T*A has already
+                // been applied to Stroker/Dasher's input. Then Ainv will be
+                // applied. Ainv*T*A is not equal to T, but it is a translation,
+                // which means that none of stroker's assumptions about its
+                // input will be violated. After all this, A will be applied
+                // to stroker's output.
+            }
+        }
 
-                    // by now strokerat == tx. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them, then they will be normalized, and then
-                    // the inverse of *only the non translation part of tx* will
-                    // be applied to the normalized paths. This won't cause problems
-                    // in stroker, because, suppose tx = T*A, where T is just the
-                    // translation part of tx, and A is the rest. T*A has already
-                    // been applied to Stroker/Dasher's input. Then Ainv will be
-                    // applied. Ainv*T*A is not equal to T, but it is a translation,
-                    // which means that none of stroker's assumptions about its
-                    // input will be violated. After all this, A will be applied
-                    // to stroker's output.
-                }
-            }
+        // Get renderer offsets:
+        float rdrOffX = 0.0f, rdrOffY = 0.0f;
+
+        if (rdrCtx.doClip && (tx != null)) {
+            final MarlinRenderer renderer = (MarlinRenderer)out;
+            rdrOffX = renderer.getOffsetX();
+            rdrOffY = renderer.getOffsetY();
         }
 
         // Prepare the pipeline:
         PathConsumer2D pc = out;
 
+        final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+        if (DO_TRACE_PATH) {
+            // trace Stroker:
+            pc = transformerPC2D.traceStroker(pc);
+        }
+
         if (MarlinConst.USE_SIMPLIFIER) {
             // Use simplifier after stroker before Renderer
             // to remove collinear segments (notably due to cap square)
             pc = rdrCtx.simplifier.init(pc);
         }
 
-        final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+        // deltaTransformConsumer may adjust the clip rectangle:
+        pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx, rdrOffX, rdrOffY);
 
-        if (stroke != null) {
-            pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
+        // stroker will adjust the clip rectangle (width / miter limit):
+        pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
+                stroke.getLineJoin(), stroke.getMiterLimit(),
+                scale, rdrOffX, rdrOffY);
 
-            pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
-                    stroke.getLineJoin(), stroke.getMiterLimit());
+        if (dashes != null) {
+            if (!recycleDashes) {
+                dashLen = dashes.length;
+            }
+            pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes);
+        } else if (rdrCtx.doClip && (stroke.getEndCap() != Stroker.CAP_BUTT)) {
+            if (DO_TRACE_PATH) {
+                pc = transformerPC2D.traceClosedPathDetector(pc);
+            }
 
-            if (dashes != null) {
-                if (!recycleDashes) {
-                    dashLen = dashes.length;
-                }
-                pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes);
-            }
-            pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
+            // If no dash and clip is enabled:
+            // detect closedPaths (polygons) for caps
+            pc = transformerPC2D.detectClosedPath(pc);
         }
+        pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
 
+        if (DO_TRACE_PATH) {
+            // trace Input:
+            pc = transformerPC2D.traceInput(pc);
+        }
         /*
          * Pipeline seems to be:
          * shape.getPathIterator(tx)
@@ -185,18 +216,52 @@
             final int piRule,
             final MarlinRenderer renderer)
     {
-        final int oprule = ((stroke == null) && (piRule == PathIterator.WIND_EVEN_ODD)) ?
-            MarlinRenderer.WIND_EVEN_ODD : MarlinRenderer.WIND_NON_ZERO;
+        if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
+            // Define the initial clip bounds:
+            final float[] clipRect = rdrCtx.clipRect;
 
-        renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
+            clipRect[0] = clip.y;
+            clipRect[1] = clip.y + clip.height;
+            clipRect[2] = clip.x;
+            clipRect[3] = clip.x + clip.width;
 
-        float lw = 0.0f;
+            // Enable clipping:
+            rdrCtx.doClip = true;
+        }
 
         if (stroke != null) {
-            lw = stroke.getLineWidth();
+            renderer.init(clip.x, clip.y, clip.width, clip.height,
+                          MarlinConst.WIND_NON_ZERO);
+
+            return initStroker(rdrCtx, stroke, stroke.getLineWidth(), tx, renderer);
+        } else {
+            // Filler:
+            final int oprule = (piRule == PathIterator.WIND_EVEN_ODD) ?
+                MarlinConst.WIND_EVEN_ODD : MarlinConst.WIND_NON_ZERO;
+
+            renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
+
+            PathConsumer2D pc = renderer;
+
+            final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+            if (DO_CLIP_FILL && rdrCtx.doClip) {
+                float rdrOffX = renderer.getOffsetX();
+                float rdrOffY = renderer.getOffsetY();
+
+                if (DO_TRACE_PATH) {
+                    // trace Filler:
+                    pc = rdrCtx.transformerPC2D.traceFiller(pc);
+                }
+                pc = rdrCtx.transformerPC2D.pathClipper(pc, rdrOffX, rdrOffY);
+            }
+
+            if (DO_TRACE_PATH) {
+                // trace Input:
+                pc = transformerPC2D.traceInput(pc);
+            }
+            return pc;
         }
-
-        return initPipeline(rdrCtx, stroke, lw, tx, renderer);
     }
 
     public static MarlinRenderer setupRenderer(
@@ -232,7 +297,7 @@
             final float lineWidth,
             final PathConsumer2D out)
     {
-        final PathConsumer2D pc2d = initPipeline(rdrCtx, stroke, lineWidth, null, out);
+        final PathConsumer2D pc2d = initStroker(rdrCtx, stroke, lineWidth, null, out);
 
         if (shape instanceof Path2D) {
             feedConsumer(rdrCtx, (Path2D)shape, null, pc2d);
--- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/ShapeUtil.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/ShapeUtil.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/javafx/animation/PathTransition.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/animation/PathTransition.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/javafx/animation/PauseTransition.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/animation/PauseTransition.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/javafx/animation/ScaleTransition.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/animation/ScaleTransition.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/javafx/animation/TranslateTransition.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/animation/TranslateTransition.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/java/javafx/scene/Node.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/Node.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1055,15 +1055,13 @@
         }
     }
 
-    // reapplyCSS should be true for root elements when they are added, and is false for children
-    // of the root element. This prevents CSS being reapplied recursively, as noted in JDK-8151756.
-    private void invalidatedScenes(Scene oldScene, SubScene oldSubScene, boolean reapplyCSS) {
+    private void invalidatedScenes(Scene oldScene, SubScene oldSubScene) {
         Scene newScene = sceneProperty().get();
         boolean sceneChanged = oldScene != newScene;
         SubScene newSubScene = subScene;
 
         if (getClip() != null) {
-            getClip().setScenes(newScene, newSubScene, reapplyCSS);
+            getClip().setScenes(newScene, newSubScene);
         }
         if (sceneChanged) {
             updateCanReceiveFocus();
@@ -1086,7 +1084,7 @@
         }
         updateTreeShowing();
 
-        if (sceneChanged && reapplyCSS) reapplyCSS();
+        if (sceneChanged) reapplyCSS();
 
         if (sceneChanged && !isDirtyEmpty()) {
             //Note: no need to remove from scene's dirty list
@@ -1145,16 +1143,16 @@
         }
     }
 
-    final void setScenes(Scene newScene, SubScene newSubScene, boolean reapplyCSS) {
+    final void setScenes(Scene newScene, SubScene newSubScene) {
         Scene oldScene = sceneProperty().get();
         if (newScene != oldScene || newSubScene != subScene) {
             scene.set(newScene);
             SubScene oldSubScene = subScene;
             subScene = newSubScene;
-            invalidatedScenes(oldScene, oldSubScene, reapplyCSS);
+            invalidatedScenes(oldScene, oldSubScene);
             if (this instanceof SubScene) { // TODO: find better solution
                 SubScene thisSubScene = (SubScene)this;
-                thisSubScene.getRoot().setScenes(newScene, thisSubScene, reapplyCSS);
+                thisSubScene.getRoot().setScenes(newScene, thisSubScene);
             }
         }
     }
@@ -6953,13 +6951,13 @@
                         } else {
                             if (oldClip != null) {
                                 oldClip.clipParent = null;
-                                oldClip.setScenes(null, null, /* reapplyCSS */ false);
+                                oldClip.setScenes(null, null);
                                 oldClip.updateTreeVisible(false);
                             }
 
                             if (newClip != null) {
                                 newClip.clipParent = Node.this;
-                                newClip.setScenes(getScene(), getSubScene(), /* reapplyCSS */ false);
+                                newClip.setScenes(getScene(), getSubScene());
                                 newClip.updateTreeVisible(true);
                             }
 
--- a/modules/javafx.graphics/src/main/java/javafx/scene/Parent.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/Parent.java	Wed Dec 20 12:22:56 2017 -0800
@@ -373,7 +373,7 @@
                             relayout = true;
                         }
                         node.setParent(Parent.this);
-                        node.setScenes(getScene(), getSubScene(), /* reapplyCSS*/ true);
+                        node.setScenes(getScene(), getSubScene());
                         // assert !node.boundsChanged;
                         if (node.isVisible()) {
                             geomChanged = true;
@@ -606,7 +606,7 @@
                     }
                     if (old.getParent() == Parent.this) {
                         old.setParent(null);
-                        old.setScenes(null, null, /* reapplyCSS*/ false);
+                        old.setScenes(null, null);
                     }
                     // Do not add node with null scene to the removed list.
                     // It will not be processed in the list and its memory
@@ -773,7 +773,7 @@
         }
 
         for (int i=0; i<children.size(); i++) {
-            children.get(i).setScenes(newScene, newSubScene, /* reapplyCSS*/ false);
+            children.get(i).setScenes(newScene, newSubScene);
         }
 
         final boolean awaitingLayout = layoutFlag != LayoutFlags.CLEAN;
--- a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1239,11 +1239,11 @@
                     }
 
                     if (oldRoot != null) {
-                        oldRoot.setScenes(null, null, /* reapplyCSS*/ false);
+                        oldRoot.setScenes(null, null);
                     }
                     oldRoot = _value;
                     _value.getStyleClass().add(0, "root");
-                    _value.setScenes(Scene.this, null, /* reapplyCSS*/ true);
+                    _value.setScenes(Scene.this, null);
                     markDirty(DirtyBits.ROOT_DIRTY);
                     _value.resize(getWidth(), getHeight()); // maybe no-op if root is not resizable
                     _value.requestLayout();
--- a/modules/javafx.graphics/src/main/java/javafx/scene/SubScene.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/SubScene.java	Wed Dec 20 12:22:56 2017 -0800
@@ -320,11 +320,11 @@
 
                     if (oldRoot != null) {
                         StyleManager.getInstance().forget(SubScene.this);
-                        oldRoot.setScenes(null, null, /* reapplyCSS*/ false);
+                        oldRoot.setScenes(null, null);
                     }
                     oldRoot = _value;
                     _value.getStyleClass().add(0, "root");
-                    _value.setScenes(getScene(), SubScene.this, /* reapplyCSS*/ true);
+                    _value.setScenes(getScene(), SubScene.this);
                     markDirty(SubSceneDirtyBits.ROOT_SG_DIRTY);
                     _value.resize(getWidth(), getHeight()); // maybe no-op if root is not resizable
                     _value.requestLayout();
--- a/modules/javafx.graphics/src/main/java/javafx/scene/text/TextBoundsType.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/text/TextBoundsType.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-font/coretext.c	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-font/coretext.c	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -568,6 +568,14 @@
 bool WindowContextBase::set_view(jobject view) {
 
     if (jview) {
+        mainEnv->CallVoidMethod(jview, jViewNotifyMouse,
+                com_sun_glass_events_MouseEvent_EXIT,
+                com_sun_glass_events_MouseEvent_BUTTON_NONE,
+                0, 0,
+                0, 0,
+                0,
+                JNI_FALSE,
+                JNI_FALSE);
         mainEnv->DeleteGlobalRef(jview);
     }
 
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassCursor.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassCursor.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassDialogs.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassDialogs.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassEmbeddedWindow+Npapi.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassEmbeddedWindow+Npapi.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassHelper.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassHelper.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassMenu.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassMenu.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassOffscreen.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassOffscreen.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassRobot.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassRobot.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-prism-d3d/D3DPipelineManager.cc	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-prism-d3d/D3DPipelineManager.cc	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-prism-d3d/Trace.cc	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-prism-d3d/Trace.cc	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-prism-es2/eglWrapper/eglWrapper.c	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-prism-es2/eglWrapper/eglWrapper.c	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/main/native-prism-sw/JPiscesRenderer.c	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/main/native-prism-sw/JPiscesRenderer.c	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/font/PrismFontFactoryTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/font/PrismFontFactoryTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubImageLoader.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubImageLoader.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubImageLoaderFactory.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubImageLoaderFactory.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.media/src/main/java/com/sun/media/jfxmediaimpl/platform/gstreamer/GSTPlatform.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.media/src/main/java/com/sun/media/jfxmediaimpl/platform/gstreamer/GSTPlatform.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.media/src/main/java/javafx/scene/media/MediaMarkerEvent.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.media/src/main/java/javafx/scene/media/MediaMarkerEvent.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/Disposer.java	Wed Dec 20 12:22:56 2017 -0800
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.embed.swing;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Hashtable;
+
+/**
+ * This class is used for registering and disposing the native
+ * data associated with java objects.
+ *
+ * The object can register itself by calling the addRecord method and
+ * providing a descendant of the DisposerRecord class with overridden
+ * dispose() method.
+ *
+ * When the object becomes unreachable, the dispose() method
+ * of the associated DisposerRecord object will be called.
+ *
+ * @see DisposerRecord
+ */
+public class Disposer implements Runnable {
+    private static final ReferenceQueue queue = new ReferenceQueue();
+    private static final Hashtable records = new Hashtable();
+    private static Disposer disposerInstance;
+
+    static {
+        disposerInstance = new Disposer();
+
+        ThreadGroup tg = Thread.currentThread().getThreadGroup();
+        java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction() {
+                public Object run() {
+                    /* The thread must be a member of a thread group
+                     * which will not get GCed before VM exit.
+                     * Make its parent the top-level thread group.
+                     */
+                    ThreadGroup tg = Thread.currentThread().getThreadGroup();
+                    for (ThreadGroup tgn = tg;
+                         tgn != null;
+                         tg = tgn, tgn = tg.getParent());
+                    Thread t =
+                        new Thread(tg, disposerInstance, "SwingNode Disposer");
+                    t.setContextClassLoader(null);
+                    t.setDaemon(true);
+                    t.setPriority(Thread.MAX_PRIORITY);
+                    t.start();
+                    return null;
+                }
+            }
+        );
+    }
+
+    /**
+     * Registers the object and the native data for later disposal.
+     * @param target Object to be registered
+     * @param rec the associated DisposerRecord object
+     * @see DisposerRecord
+     */
+    public static WeakReference addRecord(Object target, DisposerRecord rec) {
+        WeakReference ref = new WeakReference(target, queue);
+        disposerInstance.records.put(ref, rec);
+        return ref;
+    }
+
+    public void run() {
+        while (true) {
+            try {
+                Object obj = queue.remove();
+                ((Reference)obj).clear();
+                DisposerRecord rec = (DisposerRecord)records.remove(obj);
+                rec.dispose();
+                obj = null;
+                rec = null;
+            } catch (Exception e) {
+                System.out.println("Exception while removing reference: " + e);
+                e.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/DisposerRecord.java	Wed Dec 20 12:22:56 2017 -0800
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.embed.swing;
+
+/**
+ * This class is used to hold the resource to be disposed.
+ */
+public interface DisposerRecord {
+    public void dispose();
+}
--- a/modules/javafx.swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.swing/src/main/java/javafx/embed/swing/FXDnD.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/FXDnD.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingNode.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingNode.java	Wed Dec 20 12:22:56 2017 -0800
@@ -46,6 +46,7 @@
 import java.awt.Cursor;
 import java.awt.EventQueue;
 import java.awt.Toolkit;
+import java.lang.ref.WeakReference;
 import java.awt.dnd.DragGestureEvent;
 import java.awt.dnd.DragGestureListener;
 import java.awt.dnd.DragGestureRecognizer;
@@ -66,6 +67,8 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
+import com.sun.javafx.embed.swing.Disposer;
+import com.sun.javafx.embed.swing.DisposerRecord;
 import com.sun.javafx.geom.BaseBounds;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.jmx.MXNodeAlgorithm;
@@ -365,17 +368,9 @@
         if (content != null) {
             lwFrame = new JLightweightFrame();
 
-            lwFrame.addWindowFocusListener(new WindowFocusListener() {
-                @Override
-                public void windowGainedFocus(WindowEvent e) {
-                    SwingFXUtils.runOnFxThread(() ->
-                                                 SwingNode.this.requestFocus());
-                }
-                @Override
-                public void windowLostFocus(WindowEvent e) {
-                    SwingFXUtils.runOnFxThread(() -> ungrabFocus(true));
-                }
-            });
+            SwingNodeWindowFocusListener snfListener =
+                                 new SwingNodeWindowFocusListener(this);
+            lwFrame.addWindowFocusListener(snfListener);
 
             if (getScene() != null) {
                 Window window = getScene().getWindow();
@@ -390,9 +385,12 @@
                     }
                 }
             }
-            lwFrame.setContent(new SwingNodeContent(content));
+            lwFrame.setContent(new SwingNodeContent(content, this));
             lwFrame.setVisible(true);
 
+            SwingNodeDisposer disposeRec = new SwingNodeDisposer(lwFrame);
+            Disposer.addRecord(this, disposeRec);
+
             if (getScene() != null) {
                 notifyNativeHandle(getScene().getWindow());
             }
@@ -812,12 +810,56 @@
         return alg.processLeafNode(this, ctx);
     }
 
-    private class SwingNodeContent implements LightweightContent {
+    private static class SwingNodeDisposer implements DisposerRecord {
+         JLightweightFrame lwFrame;
+
+         SwingNodeDisposer(JLightweightFrame ref) {
+             this.lwFrame = ref;
+         }
+         public void dispose() {
+             if (lwFrame != null) {
+                 lwFrame.dispose();
+                 lwFrame = null;
+             }
+         }
+    }
+
+    private static class SwingNodeWindowFocusListener implements WindowFocusListener {
+        private WeakReference<SwingNode> swingNodeRef;
+
+        SwingNodeWindowFocusListener(SwingNode swingNode) {
+            this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
+        }
+
+        @Override
+        public void windowGainedFocus(WindowEvent e) {
+            SwingFXUtils.runOnFxThread(() -> {
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.requestFocus();
+                }
+            });
+        }
+
+        @Override
+        public void windowLostFocus(WindowEvent e) {
+            SwingFXUtils.runOnFxThread(() -> {
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.ungrabFocus(true);
+                }
+            });
+        }
+    }
+
+    private static class SwingNodeContent implements LightweightContent {
         private JComponent comp;
         private volatile FXDnD dnd;
+        private WeakReference<SwingNode> swingNodeRef;
 
-        SwingNodeContent(JComponent comp) {
+        SwingNodeContent(JComponent comp, SwingNode swingNode) {
             this.comp = comp;
+            this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
         }
         @Override
         public JComponent getComponent() {
@@ -825,11 +867,17 @@
         }
         @Override
         public void paintLock() {
-            paintLock.lock();
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.paintLock.lock();
+            }
         }
         @Override
         public void paintUnlock() {
-            paintLock.unlock();
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.paintLock.unlock();
+            }
         }
 
         // Note: we skip @Override annotation and implement both pre-hiDPI and post-hiDPI versions
@@ -840,19 +888,31 @@
         }
         //@Override
         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) {
-            SwingNode.this.setImageBuffer(data, x, y, width, height, linestride, scale, scale);
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.setImageBuffer(data, x, y, width, height, linestride, scale, scale);
+            }
         }
         //@Override
         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) {
-            SwingNode.this.setImageBuffer(data, x, y, width, height, linestride, scaleX, scaleY);
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.setImageBuffer(data, x, y, width, height, linestride, scaleX, scaleY);
+            }
         }
         @Override
         public void imageReshaped(int x, int y, int width, int height) {
-            SwingNode.this.setImageBounds(x, y, width, height);
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.setImageBounds(x, y, width, height);
+            }
         }
         @Override
         public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
-            SwingNode.this.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+            SwingNode swingNode = swingNodeRef.get();
+            if (swingNode != null) {
+                swingNode.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+            }
         }
         @Override
         public void focusGrabbed() {
@@ -861,49 +921,68 @@
                 // so we can't delegate it to another GUI toolkit.
                 if (PlatformUtil.isLinux()) return;
 
-                if (getScene() != null &&
-                        getScene().getWindow() != null &&
-                        WindowHelper.getPeer(getScene().getWindow()) != null) {
-                    WindowHelper.getPeer(getScene().getWindow()).grabFocus();
-                    grabbed = true;
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    Scene scene = swingNode.getScene();
+                    if (scene != null &&
+                            scene.getWindow() != null &&
+                            WindowHelper.getPeer(scene.getWindow()) != null) {
+                        WindowHelper.getPeer(scene.getWindow()).grabFocus();
+                        swingNode.grabbed = true;
+                    }
                 }
             });
         }
         @Override
         public void focusUngrabbed() {
             SwingFXUtils.runOnFxThread(() -> {
-                ungrabFocus(false);
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.ungrabFocus(false);
+                }
             });
         }
         @Override
         public void preferredSizeChanged(final int width, final int height) {
             SwingFXUtils.runOnFxThread(() -> {
-                SwingNode.this.swingPrefWidth = width;
-                SwingNode.this.swingPrefHeight = height;
-                NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.swingPrefWidth = width;
+                    swingNode.swingPrefHeight = height;
+                    NodeHelper.notifyLayoutBoundsChanged(swingNode);
+                }
             });
         }
         @Override
         public void maximumSizeChanged(final int width, final int height) {
             SwingFXUtils.runOnFxThread(() -> {
-                SwingNode.this.swingMaxWidth = width;
-                SwingNode.this.swingMaxHeight = height;
-                NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.swingMaxWidth = width;
+                    swingNode.swingMaxHeight = height;
+                    NodeHelper.notifyLayoutBoundsChanged(swingNode);
+                }
             });
         }
         @Override
         public void minimumSizeChanged(final int width, final int height) {
             SwingFXUtils.runOnFxThread(() -> {
-                SwingNode.this.swingMinWidth = width;
-                SwingNode.this.swingMinHeight = height;
-                NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.swingMinWidth = width;
+                    swingNode.swingMinHeight = height;
+                    NodeHelper.notifyLayoutBoundsChanged(swingNode);
+                }
             });
         }
 
         //@Override
         public void setCursor(Cursor cursor) {
             SwingFXUtils.runOnFxThread(() -> {
-                SwingNode.this.setCursor(SwingCursors.embedCursorToCursor(cursor));
+                SwingNode swingNode = swingNodeRef.get();
+                if (swingNode != null) {
+                    swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor));
+                }
             });
         }
 
@@ -911,7 +990,10 @@
             // This is a part of AWT API, so the method may be invoked on any thread
             synchronized (SwingNodeContent.this) {
                 if (this.dnd == null) {
-                    this.dnd = new FXDnD(SwingNode.this);
+                    SwingNode swingNode = swingNodeRef.get();
+                    if (swingNode != null) {
+                        this.dnd = new FXDnD(swingNode);
+                    }
                 }
             }
         }
--- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/UIClientImpl.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/UIClientImpl.java	Wed Dec 20 12:22:56 2017 -0800
@@ -215,6 +215,14 @@
         return "";
     }
 
+    @Override public boolean canRunBeforeUnloadConfirmPanel() {
+        return false;
+    }
+
+    @Override public boolean runBeforeUnloadConfirmPanel(String message) {
+        return false;
+    }
+
     @Override public String[] chooseFile(String initialFileName, boolean multiple, String mimeFilters) {
         // get the toplevel window
         Window win = null;
--- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/drt/DumpRenderTree.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/drt/DumpRenderTree.java	Wed Dec 20 12:22:56 2017 -0800
@@ -324,6 +324,7 @@
     private static native boolean dumpAsText();
     private static native boolean dumpChildFramesAsText();
     private static native boolean dumpBackForwardList();
+    protected static native boolean shouldStayOnPageAfterHandlingBeforeUnload();
 
     private final class DRTLoadListener implements LoadListenerClient {
         @Override
--- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/drt/UIClientImpl.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/drt/UIClientImpl.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -158,6 +158,17 @@
         return defaultValue;
     }
 
+    @Override
+    public boolean canRunBeforeUnloadConfirmPanel() {
+        return true;
+    }
+
+    @Override
+    public boolean runBeforeUnloadConfirmPanel(String message) {
+        DumpRenderTree.out.printf("CONFIRM NAVIGATION: %s\n", message);
+        return !DumpRenderTree.drt.shouldStayOnPageAfterHandlingBeforeUnload();
+    }
+
     /**
      * {@inheritDoc}
      */
--- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/prism/PrismImage.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/prism/PrismImage.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/theme/PopupMenuImpl.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/theme/PopupMenuImpl.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.web/src/main/java/com/sun/webkit/UIClient.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/UIClient.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,9 @@
     public boolean confirm(String text);
     public String prompt(String text, String defaultValue);
 
+    public boolean canRunBeforeUnloadConfirmPanel();
+    public boolean runBeforeUnloadConfirmPanel(String message);
+
     public String[] chooseFile(String initialFileName, boolean multiple, String mimeFilters);
     public void print();
 
--- a/modules/javafx.web/src/main/java/com/sun/webkit/WebPage.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/WebPage.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -2321,6 +2321,24 @@
         return null;
     }
 
+    private boolean fwkCanRunBeforeUnloadConfirmPanel() {
+        log.log(Level.FINE, "JavaScript canRunBeforeUnloadConfirmPanel()");
+
+        if (uiClient != null) {
+            return uiClient.canRunBeforeUnloadConfirmPanel();
+        }
+        return false;
+    }
+
+    private boolean fwkRunBeforeUnloadConfirmPanel(String message) {
+        log.log(Level.FINE, "JavaScript runBeforeUnloadConfirmPanel(): message = " + message);
+
+        if (uiClient != null) {
+            return uiClient.runBeforeUnloadConfirmPanel(message);
+        }
+        return false;
+    }
+
     private void fwkAddMessageToConsole(String message, int lineNumber,
             String sourceId)
     {
--- a/modules/javafx.web/src/main/java/javafx/scene/web/HTMLEditorSkin.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/java/javafx/scene/web/HTMLEditorSkin.java	Wed Dec 20 12:22:56 2017 -0800
@@ -69,6 +69,7 @@
 
 import com.sun.javafx.scene.control.skin.FXVK;
 import com.sun.javafx.scene.web.behavior.HTMLEditorBehavior;
+import com.sun.webkit.dom.HTMLDocumentImpl;
 import com.sun.webkit.WebPage;
 import com.sun.javafx.webkit.Accessor;
 
@@ -443,6 +444,7 @@
             if (newValue.doubleValue() == totalWork) {
                 cachedHTMLText = null;
                 Platform.runLater(() -> {
+                    setDesignMode("on");
                     setContentEditable(true);
                     updateToolbarState(true);
                     updateNodeOrientation();
@@ -1104,6 +1106,11 @@
         htmlBodyElement.setAttribute("contenteditable", Boolean.toString(b));
     }
 
+    private void setDesignMode(String mode) {
+        HTMLDocumentImpl htmlDocumentImpl = (HTMLDocumentImpl)webPage.getDocument(webPage.getMainFrame());
+        htmlDocumentImpl.setDesignMode(mode);
+    }
+
     private boolean getCommandState(String command) {
         return webPage.queryCommandState(command);
     }
--- a/modules/javafx.web/src/main/native/Source/WTF/wtf/unicode/java/UnicodeJava.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WTF/wtf/unicode/java/UnicodeJava.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #ifndef JSC_UNICODE_JAVA_H
 #define JSC_UNICODE_JAVA_H
--- a/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/system/java/SoundJava.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/PAL/pal/system/java/SoundJava.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 
--- a/modules/javafx.web/src/main/native/Source/WebCore/bridge/jni/jsc/BridgeUtils.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/bridge/jni/jsc/BridgeUtils.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #pragma once
 
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/java/ChromeClientJava.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/java/ChromeClientJava.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -101,6 +101,9 @@
 
 static jmethodID addMessageToConsoleMID = NULL; // WebPage
 
+static jmethodID canRunBeforeUnloadConfirmPanelMID = NULL; // WebPage
+static jmethodID runBeforeUnloadConfirmPanelMID = NULL; // WebPage
+
 static jmethodID screenToWindowMID = NULL; // WebPage
 static jmethodID windowToScreenMID = NULL; // WebPage
 
@@ -158,6 +161,17 @@
                 "(Ljava/lang/String;ILjava/lang/String;)V");
         ASSERT(addMessageToConsoleMID);
 
+
+        canRunBeforeUnloadConfirmPanelMID = env->GetMethodID(getWebPageCls(),
+                "fwkCanRunBeforeUnloadConfirmPanel",
+                "()Z");
+        ASSERT(canRunBeforeUnloadConfirmPanelMID);
+
+        runBeforeUnloadConfirmPanelMID = env->GetMethodID(getWebPageCls(),
+                "fwkRunBeforeUnloadConfirmPanel",
+                "(Ljava/lang/String;)Z");
+        ASSERT(runBeforeUnloadConfirmPanelMID);
+
         screenToWindowMID = env->GetMethodID(getWebPageCls(), "fwkScreenToWindow",
             "(Lcom/sun/webkit/graphics/WCPoint;)Lcom/sun/webkit/graphics/WCPoint;");
         ASSERT(screenToWindowMID);
@@ -536,8 +550,22 @@
 
 bool ChromeClientJava::canRunBeforeUnloadConfirmPanel()
 {
-    notImplemented();
-    return false;
+    JNIEnv* env = WebCore_GetJavaEnv();
+    initRefs(env);
+
+    auto result = env->CallBooleanMethod(m_webPage, canRunBeforeUnloadConfirmPanelMID);
+    CheckAndClearException(env);
+    return result;
+}
+
+bool ChromeClientJava::runBeforeUnloadConfirmPanel(const String& message, Frame&)
+{
+    JNIEnv* env = WebCore_GetJavaEnv();
+    initRefs(env);
+
+    auto result = env->CallBooleanMethod(m_webPage, runBeforeUnloadConfirmPanelMID, (jstring)message.toJavaString(env));
+    CheckAndClearException(env);
+    return result;
 }
 
 void ChromeClientJava::addMessageToConsole(MessageSource, MessageLevel, const String& message,
@@ -553,12 +581,6 @@
     CheckAndClearException(env);
 }
 
-bool ChromeClientJava::runBeforeUnloadConfirmPanel(const String&, Frame&)
-{
-    notImplemented();
-    return false;
-}
-
 KeyboardUIMode ChromeClientJava::keyboardUIMode()
 {
     return KeyboardAccessTabsToLinks;
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/CookieJarJava.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/CookieJarJava.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceError.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceError.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 
 #pragma once
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceRequest.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceRequest.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 
 #pragma once
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceRequestJava.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/ResourceRequestJava.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 #include "ResourceRequest.h"
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/SocketStreamHandleImpl.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/SocketStreamHandleImpl.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2009 Apple Inc. All rights reserved.
  * Copyright (C) 2009, 2011 Google Inc.  All rights reserved.
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
--- a/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/URLLoader.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/network/java/URLLoader.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 
--- a/modules/javafx.web/src/main/native/Tools/DumpRenderTree/java/DumpRenderTree.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Tools/DumpRenderTree/java/DumpRenderTree.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,6 +1,28 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
  */
+
 #include "config.h"
 
 #include <memory>
@@ -103,6 +125,13 @@
     return bool_to_jbool(gTestRunner->dumpBackForwardList());
 }
 
+JNIEXPORT jboolean JNICALL Java_com_sun_javafx_webkit_drt_DumpRenderTree_shouldStayOnPageAfterHandlingBeforeUnload
+    (JNIEnv*, jclass)
+{
+    ASSERT(gTestRunner);
+    return bool_to_jbool(gTestRunner->shouldStayOnPageAfterHandlingBeforeUnload());
+}
+
 #ifdef __cplusplus
 }
 #endif
--- a/modules/javafx.web/src/main/native/Tools/DumpRenderTree/java/TestRunnerJava.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/main/native/Tools/DumpRenderTree/java/TestRunnerJava.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 #include "JavaEnv.h"
--- a/modules/javafx.web/src/test/java/test/com/sun/webkit/SharedBufferTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/test/java/test/com/sun/webkit/SharedBufferTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.web/src/test/java/test/com/sun/webkit/SimpleSharedBufferInputStreamTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/test/java/test/com/sun/webkit/SimpleSharedBufferInputStreamTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/javafx.web/src/test/java/test/javafx/scene/web/WebPageTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/javafx.web/src/test/java/test/javafx/scene/web/WebPageTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/antplugin/java/com/sun/javafx/tools/ant/FileAssociation.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/antplugin/java/com/sun/javafx/tools/ant/FileAssociation.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/antplugin/java/com/sun/javafx/tools/ant/SecondaryLauncher.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/antplugin/java/com/sun/javafx/tools/ant/SecondaryLauncher.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/BasicBundlers.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/BasicBundlers.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/Bundlers.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/Bundlers.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/ConfigException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/ConfigException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/IOUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/IOUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/InvalidBundlerParamException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/InvalidBundlerParamException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/JreUtils.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/JreUtils.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/RelativeFileSet.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/RelativeFileSet.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/UnsupportedPlatformException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/UnsupportedPlatformException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxDebBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxDebBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxRpmBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/linux/LinuxRpmBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/mac/MacDmgBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/mac/MacDmgBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/mac/MacPkgBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/mac/MacPkgBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/oracle/tools/packager/windows/WinMsiBundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/oracle/tools/packager/windows/WinMsiBundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CachingPrintStream.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CachingPrintStream.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CommonParams.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CommonParams.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CreateBSSParams.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CreateBSSParams.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CreateJarParams.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/CreateJarParams.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/HtmlParam.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/HtmlParam.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/JSCallback.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/JSCallback.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/JarSignature.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/JarSignature.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/Log.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/Log.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/MakeAllParams.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/MakeAllParams.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/PackagerException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/PackagerException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/Param.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/Param.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/SignJarParams.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/SignJarParams.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/TemplatePlaceholders.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/TemplatePlaceholders.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/ConfigException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/ConfigException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/RelativeFileSet.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/RelativeFileSet.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/UnsupportedPlatformException.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/packager/bundlers/UnsupportedPlatformException.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ConsolidatedResources.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ConsolidatedResources.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/DeployResource.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/DeployResource.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/DetailedResourceTraversal.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/DetailedResourceTraversal.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/PackagerResource.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/PackagerResource.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ResourceFilter.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ResourceFilter.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ResourceTraversal.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/com/sun/javafx/tools/resource/ResourceTraversal.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/JDepHelper.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/JDepHelper.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/Module.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/Module.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/ModuleManager.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/ModuleManager.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/builders/linux/LinuxAppImageBuilder.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/builders/linux/LinuxAppImageBuilder.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/builders/windows/WindowsAppImageBuilder.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/builders/windows/WindowsAppImageBuilder.java	Wed Dec 20 12:22:56 2017 -0800
@@ -285,7 +285,7 @@
         AtomicReference<IOException> ioe = new AtomicReference<>();
         final String finalVsVer = vsVer;
         try (Stream<Path> files = Files.list(runtimeDir.resolve("bin"))) {
-            files.filter(p -> Pattern.matches("(vcruntime|msvcp)\\d\\d\\d.dll", p.toFile().getName().toLowerCase()))
+            files.filter(p -> Pattern.matches("^(vcruntime|msvcp|msvcr|ucrtbase|api-ms-win-).*\\.dll$", p.toFile().getName().toLowerCase()))
                  .filter(p -> !p.toString().toLowerCase().endsWith(finalVsVer + ".dll"))
                  .forEach(p -> {
                     try {
--- a/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/mac/MacCertificate.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/java/jdk/packager/internal/legacy/mac/MacCertificate.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/modules/jdk.packager/src/main/native/library/common/GenericPlatform.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/GenericPlatform.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -155,7 +155,9 @@
 TString GenericPlatform::GetAppName() {
     TString result = GetModuleFileName();
     result = FilePath::ExtractFileName(result);
+#if defined(WINDOWS)
     result = FilePath::ChangeFileExt(result, _T(""));
+#endif
     return result;
 }
 #endif //WINDOWS || LINUX
--- a/modules/jdk.packager/src/main/native/library/common/JavaVirtualMachine.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/JavaVirtualMachine.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/main/native/library/common/JavaVirtualMachine.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/JavaVirtualMachine.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/main/native/library/common/Package.h	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/Package.h	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/main/native/library/common/Platform.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/Platform.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/main/native/library/common/main.cpp	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/main/native/library/common/main.cpp	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Board.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Board.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Game.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Game.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/MinesweeperFX.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/MinesweeperFX.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Resources.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/test/apps/MinesweeperFX/src/minesweeperfx/Resources.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/modules/jdk.packager/src/test/examples/fxmodularapp/src/minesweeperfx/Resources.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/modules/jdk.packager/src/test/examples/fxmodularapp/src/minesweeperfx/Resources.java	Wed Dec 20 12:22:56 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates.
  * All rights reserved. Use is subject to license terms.
  *
  * This file is available and licensed under the following license:
--- a/tests/system/src/test/java/test/javafx/embed/swing/FXImageConversionTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/tests/system/src/test/java/test/javafx/embed/swing/FXImageConversionTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/javafx/embed/swing/SwingNodeMemoryLeakTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.javafx.embed.swing;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.embed.swing.SwingNode;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+
+import javax.swing.JLabel;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import test.util.Util;
+import static test.util.Util.TIMEOUT;
+import junit.framework.AssertionFailedError;
+import org.junit.Test;
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+
+public class SwingNodeMemoryLeakTest {
+
+    final static int TOTAL_SWINGNODE = 10;
+    static CountDownLatch launchLatch;
+    final static int GC_ATTEMPTS = 10;
+    ArrayList<WeakReference<SwingNode>> weakRefArrSN =
+                                      new ArrayList(TOTAL_SWINGNODE);
+    ArrayList<WeakReference<JLabel>> weakRefArrJL =
+                                      new ArrayList(TOTAL_SWINGNODE);
+
+    @BeforeClass
+    public static void setupOnce() {
+        launchLatch = new CountDownLatch(1);
+        // Start the Application
+        new Thread(() -> Application.launch(SwingNodeMemoryLeakTest.MyApp.class, (String[])null)).start();
+
+        try {
+            if (!launchLatch.await(5 * TIMEOUT, TimeUnit.MILLISECONDS)) {
+                throw new AssertionFailedError("Timeout waiting for Application to launch ("+
+                    (5 * TIMEOUT) + " seconds)");
+            }
+        } catch (InterruptedException ex) {
+            AssertionFailedError err = new AssertionFailedError("Unexpected exception");
+            err.initCause(ex);
+            throw err;
+        }
+    }
+
+    @AfterClass
+    public static void teardownOnce() {
+        Platform.exit();
+    }
+
+    @Test
+    public void testSwingNodeMemoryLeak() {
+        Util.runAndWait(() -> {
+            testSwingNodeObjectsInStage();
+        });
+        attemptGCSwingNode();
+        assertEquals(TOTAL_SWINGNODE, getCleanedUpSwingNodeCount());
+
+        attemptGCJLabel();
+        assertEquals(TOTAL_SWINGNODE, getCleanedUpJLabelCount());
+    }
+
+    private void testSwingNodeObjectsInStage() {
+
+        Stage tempStage[] = new Stage[TOTAL_SWINGNODE];
+
+        for (int i = 0; i < TOTAL_SWINGNODE; i++) {
+            BorderPane root = new BorderPane();
+            SwingNode sw = new SwingNode();
+            JLabel label = new JLabel("SWING");
+            sw.setContent(label);
+
+            weakRefArrSN.add(i, new WeakReference<SwingNode>(sw));
+            weakRefArrJL.add(i, new WeakReference<JLabel>(label));
+
+            root.centerProperty().set(sw);
+
+            Stage stage = new Stage();
+            Scene scene = new Scene(root, 150, 100);
+            stage.setScene(scene);
+
+            tempStage[i] = stage;
+        }
+        if (TOTAL_SWINGNODE != weakRefArrSN.size()) {
+            System.out.println("TOTAL_SWINGNODE != weakRefArr.size()");
+        }
+        assertEquals(0, getCleanedUpSwingNodeCount());
+        assertEquals(0, getCleanedUpJLabelCount());
+        assertEquals(TOTAL_SWINGNODE, weakRefArrSN.size());
+        assertEquals(TOTAL_SWINGNODE, weakRefArrJL.size());
+
+        for (int i = 0; i < TOTAL_SWINGNODE; i++) {
+            if (tempStage[i] != null) {
+                tempStage[i].close();
+                tempStage[i] = null;
+            }
+        }
+    }
+
+    private void attemptGCSwingNode() {
+        // Attempt gc GC_ATTEMPTS times
+        for (int i = 0; i < GC_ATTEMPTS; i++) {
+            System.gc();
+            System.runFinalization();
+            if (getCleanedUpSwingNodeCount() == TOTAL_SWINGNODE) {
+                break;
+            }
+            try {
+                Thread.sleep(250);
+            } catch (InterruptedException e) {
+                System.err.println("InterruptedException occurred during Thread.sleep()");
+            }
+        }
+    }
+
+    private void attemptGCJLabel() {
+        // Attempt gc GC_ATTEMPTS times
+        for (int i = 0; i < GC_ATTEMPTS; i++) {
+            System.gc();
+            System.runFinalization();
+            if (getCleanedUpJLabelCount() == TOTAL_SWINGNODE) {
+                break;
+            }
+            try {
+                Thread.sleep(250);
+            } catch (InterruptedException e) {
+                System.err.println("InterruptedException occurred during Thread.sleep()");
+            }
+        }
+    }
+    private int getCleanedUpSwingNodeCount() {
+        int count = 0;
+        for (WeakReference<SwingNode> ref : weakRefArrSN) {
+            if (ref.get() == null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private int getCleanedUpJLabelCount() {
+        int count = 0;
+        for (WeakReference<JLabel> ref : weakRefArrJL) {
+            if (ref.get() == null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public static class MyApp extends Application {
+        @Override
+        public void start(Stage stage) throws Exception {
+            launchLatch.countDown();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/javafx/scene/web/HTMLEditorTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.javafx.scene.web;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.event.Event;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.Scene;
+import javafx.scene.web.HTMLEditor;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static javafx.concurrent.Worker.State.SUCCEEDED;
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static test.util.Util.TIMEOUT;
+
+public class HTMLEditorTest {
+    private static final CountDownLatch launchLatch = new CountDownLatch(1);
+
+    // Maintain one application instance
+    static HTMLEditorTestApp htmlEditorTestApp;
+
+    public static class HTMLEditorTestApp extends Application {
+        Stage primaryStage = null;
+
+        public HTMLEditorTestApp() {
+            super();
+        }
+
+        @Override
+        public void init() {
+            HTMLEditorTest.htmlEditorTestApp = this;
+        }
+
+        @Override
+        public void start(Stage primaryStage) throws Exception {
+            this.primaryStage = primaryStage;
+            launchLatch.countDown();
+        }
+    }
+
+    @BeforeClass
+    public static void setupOnce() {
+        // Start the Test Application
+        new Thread(() -> Application.launch(HTMLEditorTestApp.class,
+            (String[]) null)).start();
+
+        try {
+            if (!launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                fail("Timeout waiting for FX runtime to start");
+            }
+        } catch (InterruptedException exception) {
+            fail("Unexpected exception: " + exception);
+        }
+
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        Platform.exit();
+    }
+
+    /**
+     * @test
+     * @bug 8090011
+     * Summary Check document focus change behavior on tab key press
+     */
+    @Test
+    public void checkFocusChange() throws Exception {
+        final CountDownLatch editorStateLatch = new CountDownLatch(2);
+        final AtomicBoolean result = new AtomicBoolean(false);
+        Platform.runLater(() -> {
+            HTMLEditor htmlEditor = new HTMLEditor();
+            Scene scene = new Scene(htmlEditor);
+            htmlEditorTestApp.primaryStage.setScene(scene);
+            WebView webView = (WebView)htmlEditor.lookup(".web-view");
+            assertNotNull(webView);
+
+            KeyEvent tabKeyEvent = new KeyEvent(null, webView,
+                                KeyEvent.KEY_PRESSED, "", "",
+                                KeyCode.TAB, false, false, false, false);
+
+            webView.focusedProperty().
+                addListener((observable, oldValue, newValue) -> {
+                if (newValue) {
+                    webView.getEngine().
+                        executeScript("document.body.focus();");
+                    // Check focus change on repeated tab key press
+                    for (int i = 0; i < 10; ++i) {
+                        Event.fireEvent(webView, tabKeyEvent);
+                    }
+                    result.set("red".equals(webView.getEngine().
+                        executeScript("document.body.style.backgroundColor").
+                        toString()));
+                    htmlEditorTestApp.primaryStage.hide();
+                    editorStateLatch.countDown();
+                }
+            });
+
+            webView.getEngine().getLoadWorker().stateProperty().
+                addListener((observable, oldValue, newValue) -> {
+                if (newValue == SUCCEEDED) {
+                    webView.getEngine().executeScript(
+                        "document.body.style.backgroundColor='red';" +
+                        "document.body.onfocusout = function() {" +
+                        "document.body.style.backgroundColor = 'yellow';" +
+                        "}");
+                    htmlEditor.requestFocus();
+                    editorStateLatch.countDown();
+                }
+            });
+            htmlEditorTestApp.primaryStage.show();
+        });
+
+        try {
+            editorStateLatch.await(10, TimeUnit.SECONDS);
+        } catch (InterruptedException ex) {
+            throw new AssertionError(ex);
+        } finally {
+            assertTrue("Focus Change with design mode enabled ", result.get());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/robot/javafx/scene/SceneChangeEventsTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.robot.javafx.scene;
+
+import com.sun.glass.ui.Robot;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.WindowEvent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/*
+ * Test to verify the events when scene of stage is changed.
+ * Steps of Test:
+ * 1. Create a stage with a scene with a button.
+ * 2. Add MOUSE_EXITED listener & WindowProperty Listener to the same scene.
+ * 3. In onAction of the button change stage's scene to a new scene.
+ * 4. Verify that MOUSE_EXITED & WindowPropery change listener are called in
+ *    sequence.
+ */
+
+public class SceneChangeEventsTest {
+    static CountDownLatch startupLatch;
+    static Robot robot;
+    static volatile Stage stage;
+    boolean mouseExited = false;
+    boolean mouseWindowEventOrder = false;
+    boolean windowChanged = false;
+
+    public static void main(String[] args) {
+        SceneChangeEventsTest test = new SceneChangeEventsTest();
+        test.testSceneChange();
+        exit();
+    }
+
+    @Test
+    public void testSceneChange() {
+
+        Button button = new Button("onAction");
+        CountDownLatch onActionLatch = new CountDownLatch(1);
+        button.setOnAction(event -> {
+            stage.setScene(new Scene(new HBox()));
+            onActionLatch.countDown();
+        });
+        HBox root = new HBox();
+        root.getChildren().add(button);
+        Scene scene = new Scene(root);
+        CountDownLatch setSceneLatch = new CountDownLatch(1);
+        stage.sceneProperty().addListener(observable -> setSceneLatch.countDown());
+        Platform.runLater(() -> {
+            stage.setScene(scene);
+        });
+        waitForLatch(setSceneLatch, 5, "Timeout while waiting for scene to be set on stage.");
+
+        scene.setOnMouseExited(event -> {
+            mouseExited = true;
+        });
+        scene.windowProperty().addListener(observable -> {
+            mouseWindowEventOrder = mouseExited;
+            windowChanged = true;
+        });
+        Platform.runLater(() -> {
+            robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + button.getLayoutX() + button.getLayoutBounds().getWidth() / 2),
+                    (int)(scene.getWindow().getY() + scene.getY() +  button.getLayoutY() + button.getLayoutBounds().getHeight() / 2));
+            robot.mousePress(Robot.MOUSE_LEFT_BTN);
+            robot.mouseRelease(Robot.MOUSE_LEFT_BTN);
+        });
+        waitForLatch(onActionLatch, 5, "Timeout while waiting for button.onAction().");
+
+        Assert.assertTrue("MOUSE_EXITED should be received when scene is " +
+            " changed.", mouseExited);
+        Assert.assertTrue("scene.windowProperty() listener should be received" +
+            "on scene change.", windowChanged);
+        Assert.assertTrue("MOUSE_EXITED should have been received before " +
+            "scene.windowProperty().", mouseWindowEventOrder);
+    }
+
+    public static class TestApp extends Application {
+        @Override
+        public void start(Stage primaryStage) {
+            robot = com.sun.glass.ui.Application.GetApplication().createRobot();
+            stage = primaryStage;
+            stage.initStyle(StageStyle.UNDECORATED);
+            stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
+                    Platform.runLater(startupLatch::countDown));
+            stage.setAlwaysOnTop(true);
+            stage.show();
+        }
+    }
+
+    @BeforeClass
+    public static void initFX() {
+        startupLatch = new CountDownLatch(1);
+        new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
+        waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start");
+    }
+
+    @AfterClass
+    public static void exit() {
+        Platform.runLater(() -> {
+            stage.hide();
+        });
+        Platform.exit();
+    }
+
+    public static void waitForLatch(CountDownLatch latch, int seconds, String msg) {
+        try {
+            if (!latch.await(seconds, TimeUnit.SECONDS)) {
+                fail(msg);
+            }
+        } catch (Exception ex) {
+            fail("Unexpected exception: " + ex);
+        }
+    }
+}
--- a/tests/system/src/test/java/test/robot/javafx/scene/TabPaneDragPolicyTest.java	Fri Dec 08 13:01:12 2017 -0800
+++ b/tests/system/src/test/java/test/robot/javafx/scene/TabPaneDragPolicyTest.java	Wed Dec 20 12:22:56 2017 -0800
@@ -47,6 +47,8 @@
 import org.junit.Test;
 import static org.junit.Assert.fail;
 
+import test.util.Util;
+
 /*
  * Unit test for verifying DragPolicies.
  *
@@ -113,7 +115,6 @@
         @Override
         public void onChanged(Change<? extends Tab> c) {
             listenerTestResult = false;
-            changeListenerLatch.countDown();
         };
     }
 
@@ -137,8 +138,7 @@
     @Test
     public void testReorderTop() {
         expectedTab = tabs[1];
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
-        tabPane.setSide(Side.TOP);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.TOP);
         tabPane.getTabs().addListener(reorderListener);
         testReorder(DX, DY, 1, 0, false);
         tabPane.getTabs().removeListener(reorderListener);
@@ -151,8 +151,7 @@
     @Test
     public void testReorderBottom() {
         expectedTab = tabs[1];
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
-        tabPane.setSide(Side.BOTTOM);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.BOTTOM);
         tabPane.getTabs().addListener(reorderListener);
         testReorder(DX, SCENE_HEIGHT - DY, 1, 0, false);
         tabPane.getTabs().removeListener(reorderListener);
@@ -165,8 +164,7 @@
     @Test
     public void testReorderLeft() {
         expectedTab = tabs[1];
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
-        tabPane.setSide(Side.LEFT);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.LEFT);
         tabPane.getTabs().addListener(reorderListener);
         testReorder(DX, DY, 0, 1, false);
         tabPane.getTabs().removeListener(reorderListener);
@@ -179,8 +177,7 @@
     @Test
     public void testReorderRight() {
         expectedTab = tabs[1];
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
-        tabPane.setSide(Side.RIGHT);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.RIGHT);
         tabPane.getTabs().addListener(reorderListener);
         testReorder(SCENE_WIDTH - DX, DY, 0, 1, false);
         tabPane.getTabs().removeListener(reorderListener);
@@ -194,8 +191,7 @@
     public void testFixedTop() {
         expectedTab = tabs[0];
         listenerTestResult = true;
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.FIXED);
-        tabPane.setSide(Side.TOP);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.TOP);
         tabPane.getTabs().addListener(fixedListener);
         testReorder(DX, DY, 1, 0, true);
         tabPane.getTabs().removeListener(fixedListener);
@@ -209,8 +205,7 @@
     public void testFixedBottom() {
         expectedTab = tabs[0];
         listenerTestResult = true;
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.FIXED);
-        tabPane.setSide(Side.BOTTOM);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.BOTTOM);
         tabPane.getTabs().addListener(fixedListener);
         testReorder(DX, SCENE_HEIGHT - DY, 1, 0, true);
         tabPane.getTabs().removeListener(fixedListener);
@@ -224,8 +219,7 @@
     public void testFixedLeft() {
         expectedTab = tabs[0];
         listenerTestResult = true;
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.FIXED);
-        tabPane.setSide(Side.LEFT);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.LEFT);
         tabPane.getTabs().addListener(fixedListener);
         testReorder(DX, DY, 0, 1, true);
         tabPane.getTabs().removeListener(fixedListener);
@@ -239,8 +233,7 @@
     public void testFixedRight() {
         expectedTab = tabs[0];
         listenerTestResult = true;
-        tabPane.setTabDragPolicy(TabPane.TabDragPolicy.FIXED);
-        tabPane.setSide(Side.RIGHT);
+        setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.RIGHT);
         tabPane.getTabs().addListener(fixedListener);
         testReorder(SCENE_WIDTH - DX, DY, 0, 1, true);
         tabPane.getTabs().removeListener(fixedListener);
@@ -310,7 +303,11 @@
 
         if (isFixed) {
             // For FIXED drag policy, tabs[0] should remain the first tab.
-            waitForLatch(changeListenerLatch, 1, "Timeout waiting ChangeListener to get called.");
+            try {
+                Thread.sleep(500); // Wait for ChangeListener to get called.
+            } catch (Exception ex) {
+                fail("Thread was interrupted." + ex);
+            }
             waitForLatch(latches[0], 5, "Timeout waiting tabs[0] to get selected.");
         } else {
             // For REORDER drag policy, tabs[1] should be the first tab.
@@ -395,10 +392,17 @@
     public static void waitForLatch(CountDownLatch latch, int seconds, String msg) {
         try {
             if (!latch.await(seconds, TimeUnit.SECONDS)) {
-                System.out.println(msg);
+                fail(msg);
             }
         } catch (Exception ex) {
             fail("Unexpected exception: " + ex);
         }
     }
+
+    public void setDragPolicyAndSide(TabPane.TabDragPolicy dragPolicy, Side side) {
+        Util.runAndWait(() -> {
+            tabPane.setTabDragPolicy(dragPolicy);
+            tabPane.setSide(side);
+        });
+    }
 }