├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── rasterPerf.log ├── results └── integration-tests.log └── src ├── main ├── java │ ├── com │ │ └── sun │ │ │ ├── javafx │ │ │ ├── geom │ │ │ │ └── Path2D.java │ │ │ └── sg │ │ │ │ └── prism │ │ │ │ ├── NGCanvas.java │ │ │ │ └── NGCanvasPath.java │ │ │ ├── marlin │ │ │ ├── ArrayCacheConst.java │ │ │ ├── ByteArrayCache.java │ │ │ ├── CollinearSimplifier.java │ │ │ ├── Curve.java │ │ │ ├── DCollinearSimplifier.java │ │ │ ├── DCurve.java │ │ │ ├── DDasher.java │ │ │ ├── DHelpers.java │ │ │ ├── DMarlinRenderer.java │ │ │ ├── DMarlinRenderingEngine.java │ │ │ ├── DPathConsumer2D.java │ │ │ ├── DPathSimplifier.java │ │ │ ├── DRenderer.java │ │ │ ├── DRendererContext.java │ │ │ ├── DRendererNoAA.java │ │ │ ├── DStroker.java │ │ │ ├── DTransformingPathConsumer2D.java │ │ │ ├── Dasher.java │ │ │ ├── DoubleArrayCache.java │ │ │ ├── FloatArrayCache.java │ │ │ ├── FloatMath.java │ │ │ ├── Helpers.java │ │ │ ├── IntArrayCache.java │ │ │ ├── MarlinAlphaConsumer.java │ │ │ ├── MarlinConst.java │ │ │ ├── MarlinProperties.java │ │ │ ├── MarlinRenderer.java │ │ │ ├── MarlinRenderingEngine.java │ │ │ ├── MarlinUtils.java │ │ │ ├── MaskMarlinAlphaConsumer.java │ │ │ ├── MergeSort.java │ │ │ ├── OffHeapArray.java │ │ │ ├── PathSimplifier.java │ │ │ ├── Renderer.java │ │ │ ├── RendererContext.java │ │ │ ├── RendererNoAA.java │ │ │ ├── RendererStats.java │ │ │ ├── Stroker.java │ │ │ ├── TransformingPathConsumer2D.java │ │ │ ├── Version.java │ │ │ ├── conv.sh │ │ │ ├── makeIntFloatClasses.sh │ │ │ └── stats │ │ │ │ ├── Histogram.java │ │ │ │ ├── Monitor.java │ │ │ │ └── StatLong.java │ │ │ ├── prism │ │ │ ├── BasicStroke.java │ │ │ └── impl │ │ │ │ ├── ps │ │ │ │ └── CachingShapeRep.java │ │ │ │ └── shape │ │ │ │ ├── DMarlinPrismUtils.java │ │ │ │ ├── DMarlinRasterizer.java │ │ │ │ ├── MarlinPrismUtils.java │ │ │ │ ├── MarlinRasterizer.java │ │ │ │ └── ShapeUtil.java │ │ │ └── util │ │ │ └── reentrant │ │ │ ├── ReentrantContext.java │ │ │ ├── ReentrantContextProvider.java │ │ │ ├── ReentrantContextProviderCLQ.java │ │ │ └── ReentrantContextProviderTL.java │ └── test │ │ ├── BigLeftSide.java │ │ ├── PathApp.java │ │ ├── PathBug.java │ │ ├── PolygonClipTest.java │ │ ├── PolygonTest.java │ │ ├── PolylineClipTest.java │ │ ├── PolylineWideClipTest.java │ │ ├── RasterPerf.java │ │ ├── ShapeOutlineBug.java │ │ ├── ShapeOutlineBugCirclePath.java │ │ ├── ShapeOutlineBugRectangle.java │ │ ├── ShapePerformanceBug.java │ │ ├── TestNonAARasterization.java │ │ ├── TextTest.java │ │ ├── TrianglePerformanceTest.java │ │ └── WebViewTest.java └── resources │ └── com │ └── sun │ └── marlin │ └── Version.properties └── test ├── java └── test │ ├── com │ └── sun │ │ ├── javafx │ │ └── geom │ │ │ └── Path2DGrowTest.java │ │ └── marlin │ │ ├── ClipShapeTest.java │ │ ├── DashedRectTest.java │ │ ├── QPathTest.java │ │ └── ScaleClipTest.java │ └── util │ └── Util.java └── resources ├── GUIMark 2 - HTML5 Vector Test.html ├── GUIMark 2 - HTML5 Vector Test_fichiers └── ga.js └── logging.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /*.sh 2 | /*.png 3 | dependency-reduced-pom.xml 4 | /target/ 5 | nbactions.xml 6 | nb-configuration.xml 7 | nbactions-deployer.xml 8 | /nbproject/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MarlinFX renderer 2 | ================= 3 | 4 | MarlinFX is the JavaFX port of the [Marlin-renderer](https://github.com/bourgesl/marlin-renderer) (scanline rasterizer only) aimed to be faster than Open/Native Pisces. 5 | 6 | Release history 7 | =============== 8 | 9 | Latest release: https://github.com/bourgesl/marlin-fx/releases/latest 10 | 11 | | JDK | Default renderer | Available MarlinFX release | 12 | | --- | --- | --- | 13 | | Open JDK 9 | MarlinFX 0.7.5 | [MarlinFX 0.9.3.1 for JDK9+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.3.1) | 14 | | Open JDK 10 | MarlinFX 0.8.2 | [MarlinFX 0.9.3.1 for JDK9+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.3.1) | 15 | | Open JFX 11 | MarlinFX 0.9.2 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 16 | | Open JFX 12 | MarlinFX 0.9.3 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 17 | | Open JFX 13 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 18 | | Open JFX 14 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 19 | | Open JFX 15 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 20 | | Open JFX 16 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 21 | | Open JFX 17 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 22 | | Open JFX 18 | MarlinFX 0.9.3.1 | [MarlinFX 0.9.4.5 for JFX11+](https://github.com/bourgesl/marlin-fx/releases/tag/v0.9.4.5) | 23 | 24 | 25 | License 26 | ======= 27 | 28 | As marlin is a fork from OpenJDK Pisces, its license is the OpenJDK's license = GPL2+CP: 29 | 30 | GNU General Public License, version 2, 31 | with the Classpath Exception 32 | 33 | The GNU General Public License (GPL) 34 | 35 | Version 2, June 1991 36 | 37 | See License.md 38 | 39 | Build 40 | ===== 41 | 42 | Needs Maven + Oracle or Open JDK 1.8 (with JavaFX) 43 | 44 | The MarlinFX build produces a (big) JavaFX jar file patched with MarlinFX classes (com.sun.marlin + hacked ShapeUtil using the MarlinRasterizer) to be placed in the boot classpath as JavaFX 8 lies in the extension classpath (and can not be patched easily). Of course, such (complete) JavaFX jar depends on your JDK version and your platform (win, mac, linux ...) so the MarlinFX jar can not be distributed (license issue) nor shared across platforms (binary incompatiblity). 45 | 46 | Note: it does not modify the SW pipeline which still uses OpenPisces (for compatiblity issue). 47 | 48 | First time, import your local jfxrt.jar (from ``${java.home}/lib/ext/jfxrt.jar``) to your local maven repository or when you get the error 'Could not find artifact javafx:jfxrt:jar:local in central (https://repo.maven.apache.org/maven2)' : 49 | 50 | ``mvn process-resources`` 51 | 52 | Then build MarlinFX: 53 | 54 | ``mvn clean install`` 55 | 56 | or 57 | 58 | ``mvn -Dmaven.test.skip=true clean install`` (to skip running tests) 59 | 60 | The MarlinFX jar is available in the target folder like: 61 | 62 | ``target/marlinfx-0.x.y-Unsafe.jar`` 63 | 64 | 65 | Usage 66 | ===== 67 | 68 | For testing purposes (only), MarlinFX can be used with any JavaFX application running on Oracle or Open JDK 8 (and derived JVMs). 69 | 70 | Just put the marlinfx-x.y.jar file in the bootclasspath to let JavaFX use MarlinFX instead of OpenPisces (java rasterizer) and set the following system property prism.marlin=true/false (true by default): 71 | 72 | ``java -Xbootclasspath/p:[absolute or relative path]/marlinfx-0.7.5-Unsafe.jar -Dprism.marlin=true ...`` 73 | 74 | For example to launch the JavaFX8 Ensemble demo: 75 | 76 | ``java -Xbootclasspath/p:/home/bourgesl/libs/marlin/branches/marlin-fx/target/marlinfx-0.7.5-Unsafe.jar -jar Ensemble8.jar`` 77 | 78 | You should see MarlinFX in action and the following message will be present in the console: 79 | ``` 80 | Marlin-FX[marlinFX-0.7.5-Unsafe-OpenJDK] (double) enabled. 81 | INFO: =============================================================================== 82 | INFO: Marlin software rasterizer = ENABLED 83 | INFO: Version = [marlinFX-0.7.5-Unsafe-OpenJDK] 84 | INFO: prism.marlin = com.sun.marlin.DRenderer 85 | INFO: prism.marlin.useThreadLocal = true 86 | INFO: prism.marlin.useRef = soft 87 | INFO: prism.marlin.edges = 4096 88 | INFO: prism.marlin.pixelsize = 2048 89 | INFO: prism.marlin.subPixel_log2_X = 3 90 | INFO: prism.marlin.subPixel_log2_Y = 3 91 | INFO: prism.marlin.blockSize_log2 = 5 92 | INFO: prism.marlin.forceRLE = false 93 | INFO: prism.marlin.forceNoRLE = false 94 | INFO: prism.marlin.useTileFlags = true 95 | INFO: prism.marlin.useTileFlags.useHeuristics = true 96 | INFO: prism.marlin.rleMinWidth = 64 97 | INFO: prism.marlin.useSimplifier = false 98 | INFO: prism.marlin.doStats = false 99 | INFO: prism.marlin.doMonitors = false 100 | INFO: prism.marlin.doChecks = true 101 | INFO: prism.marlin.log = true 102 | INFO: prism.marlin.useLogger = true 103 | INFO: prism.marlin.logCreateContext = false 104 | INFO: prism.marlin.logUnsafeMalloc = false 105 | INFO: Renderer settings: 106 | INFO: CUB_COUNT_LG = 2 107 | INFO: CUB_DEC_BND = 8.0 108 | INFO: CUB_INC_BND = 3.2 109 | INFO: QUAD_DEC_BND = 4.0 110 | INFO: INITIAL_EDGES_CAPACITY = 98304 111 | INFO: INITIAL_CROSSING_COUNT = 1024 112 | INFO: =============================================================================== 113 | ``` 114 | Two pipelines are available based on Double (default) and Float numerical values. To select the MarlinFX pipeline, use the system property prism.marlin.double=true/false (true means Double, false means Float) and is indicated in the standard output. 115 | 116 | 117 | Enjoy and send us your feedback ! 118 | 119 | Note: Marlin system properties have been renamed to use the prefix 'prism.marlin' like prism.marlin.log=true/false (true by default). 120 | 121 | 122 | Getting in touch 123 | ================ 124 | 125 | Users and developers interested in the Marlin-renderer are kindly invited to join the [marlin-renderer](https://groups.google.com/forum/#!forum/marlin-renderer) Google Group. 126 | 127 | 128 | Related projects 129 | =============== 130 | 131 | [Marlin-renderer](https://github.com/bourgesl/marlin-renderer) is the main Marlin-renderer repository (java2D rendering engine). 132 | [Mapbench](https://github.com/bourgesl/mapbench) provides benchmarking tools based on real world map painted by the [GeoServer](http://geoserver.org/) WMS server 133 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | org.marlin 9 | marlinfx 10 | jar 11 | 0.9.3.1-Unsafe 12 | Marlin software rasterizer 13 | 14 | https://github.com/bourgesl/marlin-renderer 15 | 16 | 17 | A pure Java renderer, derived from OpenJDK Pisces (GPL2+CP) 18 | 19 | 20 | 21 | yyyy/MM/dd HH:mm:ss 22 | false 23 | 24 | jfxrt.jar 25 | ${java.home}/lib/ext/${jfxrt.jar} 26 | 27 | 28 | 29 | 30 | 31 | src/main/resources 32 | true 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-resources-plugin 39 | 2.6 40 | 41 | UTF-8 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-compiler-plugin 48 | 3.6.2 49 | 50 | 1.8 51 | 1.8 52 | true 53 | UTF-8 54 | 55 | 56 | 57 | 58 | maven-install-plugin 59 | 2.5.2 60 | 61 | 62 | validate 63 | 64 | install-file 65 | 66 | 67 | javafx 68 | jfxrt 69 | local 70 | jar 71 | ${jfxrt.path} 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-shade-plugin 80 | 2.4.3 81 | 82 | 83 | package 84 | 85 | shade 86 | 87 | 88 | false 89 | 90 | 91 | javafx:jfxrt:jar:local 92 | 93 | com/sun/javafx/geom/Path2D*.class 94 | com/sun/javafx/sg/prism/NGCanvas*.class 95 | com/sun/prism/impl/BasicStroke*.class 96 | com/sun/prism/impl/ps/CachingShapeRep*.class 97 | com/sun/prism/impl/shape/ShapeUtil*.class 98 | 99 | 100 | 101 | org.marlin:marlinfx 102 | 103 | **/* 104 | 105 | 106 | test/** 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-surefire-plugin 118 | 2.20.1 119 | 120 | 1 121 | 1 122 | 123 | true 124 | 125 | -Dprism.marlin=true 126 | -DClipShapeTest.numTests=5000 127 | -Djava.util.logging.config.file=src/test/resources/logging.properties 128 | -Xbootclasspath/p:${basedir}/target/${project.build.finalName}.jar 129 | 130 | 131 | 132 | integration-tests 133 | integration-test 134 | 135 | test 136 | 137 | 138 | 139 | ${integration.skip} 140 | 141 | 142 | **/ClipShapeTest.java 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | javafx 153 | jfxrt 154 | local 155 | 156 | 157 | 158 | junit 159 | junit 160 | 4.12 161 | test 162 | 163 | 164 | org.hamcrest 165 | hamcrest-core 166 | 1.3 167 | test 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /rasterPerf.log: -------------------------------------------------------------------------------- 1 | -------------- 2 | Native Pisces: 3 | -------------- 4 | 1228697 10x10 Rectangle native non-AA rasterizations took 5290.89ms, 232228.79326540523 ops/sec 5 | 140716 100x100 Rectangle native non-AA rasterizations took 6448.01ms, 21823.167147693628 ops/sec 6 | 905292 10x10 Ellipse native non-AA rasterizations took 6311.08ms, 143444.8620521369 ops/sec 7 | 118440 100x100 Ellipse native non-AA rasterizations took 6759.62ms, 17521.695006524034 ops/sec 8 | 85947 100x100 Cubics native non-AA rasterizations took 7899.51ms, 10880.041926651147 ops/sec 9 | 148795 100x100 Quads native non-AA rasterizations took 6772.72ms, 21969.75513530753 ops/sec 10 | 11 | 574770 10x10 Rectangle native AA rasterizations took 2482.51ms, 231527.76826679448 ops/sec 12 | 57579 100x100 Rectangle native AA rasterizations took 2497.39ms, 23055.670119604867 ops/sec 13 | 360825 10x10 Ellipse native AA rasterizations took 2490.84ms, 144860.76986076985 ops/sec 14 | 43729 100x100 Ellipse native AA rasterizations took 2498.89ms, 17499.36972015575 ops/sec 15 | 27364 100x100 Cubics native AA rasterizations took 2497.62ms, 10956.030140693942 ops/sec 16 | 54946 100x100 Quads native AA rasterizations took 2499.65ms, 21981.477406836955 ops/sec 17 | 18 | -------------- 19 | Open Pisces: 20 | -------------- 21 | 1714159 10x10 Rectangle Java non-AA rasterizations took 8491.07ms, 201877.8552055277 ops/sec 22 | 111796 100x100 Rectangle Java non-AA rasterizations took 6669.15ms, 16763.155724492626 ops/sec 23 | 1043686 10x10 Ellipse Java non-AA rasterizations took 7728.42ms, 135045.19681901345 ops/sec 24 | 99690 100x100 Ellipse Java non-AA rasterizations took 7174.44ms, 13895.16115543513 ops/sec 25 | 74519 100x100 Cubics Java non-AA rasterizations took 7917.37ms, 9412.090125887764 ops/sec 26 | 128738 100x100 Quads Java non-AA rasterizations took 7561.9ms, 17024.557320250202 ops/sec 27 | 28 | 494692 10x10 Rectangle Java AA rasterizations took 2474.1ms, 199948.26401519746 ops/sec 29 | 41681 100x100 Rectangle Java AA rasterizations took 2503.33ms, 16650.22190442331 ops/sec 30 | 325037 10x10 Ellipse Java AA rasterizations took 2478.15ms, 131161.14843734237 ops/sec 31 | 34328 100x100 Ellipse Java AA rasterizations took 2501.93ms, 13720.607690862655 ops/sec 32 | 23519 100x100 Cubics Java AA rasterizations took 2499.45ms, 9409.670127428035 ops/sec 33 | 42646 100x100 Quads Java AA rasterizations took 2494.9ms, 17093.270271353562 ops/sec 34 | 35 | -------------- 36 | Marlin: 37 | -------------- 38 | 39 | 2096722 10x10 Rectangle Marlin non-AA rasterizations took 5948.66ms, 352469.63181624096 ops/sec 40 | 146539 100x100 Rectangle Marlin non-AA rasterizations took 4842.45ms, 30261.33465497837 ops/sec 41 | 32921 300x300 Rectangle Marlin non-AA rasterizations took 4427.83ms, 7435.0189596258215 ops/sec 42 | 1324006 10x10 Ellipse Marlin non-AA rasterizations took 5276.7ms, 250915.53433016848 ops/sec 43 | 88526 100x100 Ellipse Marlin non-AA rasterizations took 4015.3ms, 22047.169576370383 ops/sec 44 | 32493 300x300 Ellipse Marlin non-AA rasterizations took 4883.55ms, 6653.561446079184 ops/sec 45 | 84713 100x100 Cubics Marlin non-AA rasterizations took 5090.13ms, 16642.600483681163 ops/sec 46 | 166992 100x100 Quads Marlin non-AA rasterizations took 6500.0ms, 25691.076923076922 ops/sec 47 | 48 | 770788 10x10 Rectangle Marlin AA rasterizations took 2473.47ms, 311622.13408693054 ops/sec 49 | 72479 100x100 Rectangle Marlin AA rasterizations took 2495.7ms, 29041.551468525868 ops/sec 50 | 20650 300x300 Rectangle Marlin AA rasterizations took 2499.85ms, 8260.495629737785 ops/sec 51 | 636831 10x10 Ellipse Marlin AA rasterizations took 2476.56ms, 257143.37629615274 ops/sec 52 | 57208 100x100 Ellipse Marlin AA rasterizations took 2482.24ms, 23046.9253577414 ops/sec 53 | 16508 300x300 Ellipse Marlin AA rasterizations took 2497.73ms, 6609.201154648421 ops/sec 54 | 44000 100x100 Cubics Marlin AA rasterizations took 2496.32ms, 17625.945391616457 ops/sec 55 | 66399 100x100 Quads Marlin AA rasterizations took 2498.7ms, 26573.418177452277 ops/sec 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/com/sun/javafx/sg/prism/NGCanvasPath.java: -------------------------------------------------------------------------------- 1 | package com.sun.javafx.sg.prism; 2 | 3 | import com.sun.javafx.geom.Path2D; 4 | import com.sun.javafx.geom.Shape; 5 | import com.sun.javafx.geom.transform.BaseTransform; 6 | 7 | /** 8 | * Type needed to be handled specifically by the Marlin renderer 9 | * @author bourgesl 10 | */ 11 | public abstract class NGCanvasPath extends Shape { 12 | 13 | public abstract Path2D getGeometry(); 14 | 15 | public abstract BaseTransform getCombinedTransform(BaseTransform tx); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/ArrayCacheConst.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import java.util.Arrays; 29 | import static com.sun.marlin.MarlinUtils.logInfo; 30 | 31 | public final class ArrayCacheConst implements MarlinConst { 32 | 33 | static final int BUCKETS = 8; 34 | static final int MIN_ARRAY_SIZE = 4096; 35 | // maximum array size 36 | static final int MAX_ARRAY_SIZE; 37 | // threshold below to grow arrays by 4 38 | static final int THRESHOLD_SMALL_ARRAY_SIZE = 4 * 1024 * 1024; 39 | // threshold to grow arrays only by (3/2) instead of 2 40 | static final int THRESHOLD_ARRAY_SIZE; 41 | // threshold to grow arrays only by (5/4) instead of (3/2) 42 | static final long THRESHOLD_HUGE_ARRAY_SIZE; 43 | static final int[] ARRAY_SIZES = new int[BUCKETS]; 44 | 45 | static { 46 | // initialize buckets for int/float arrays 47 | int arraySize = MIN_ARRAY_SIZE; 48 | 49 | int inc_lg = 2; // x4 50 | 51 | for (int i = 0; i < BUCKETS; i++, arraySize <<= inc_lg) { 52 | ARRAY_SIZES[i] = arraySize; 53 | 54 | if (DO_TRACE) { 55 | logInfo("arraySize[" + i + "]: " + arraySize); 56 | } 57 | 58 | if (arraySize >= THRESHOLD_SMALL_ARRAY_SIZE) { 59 | inc_lg = 1; // x2 60 | } 61 | } 62 | MAX_ARRAY_SIZE = arraySize >> inc_lg; 63 | 64 | if (MAX_ARRAY_SIZE <= 0) { 65 | throw new IllegalStateException("Invalid max array size !"); 66 | } 67 | 68 | THRESHOLD_ARRAY_SIZE = 16 * 1024 * 1024; // >16M 69 | THRESHOLD_HUGE_ARRAY_SIZE = 48L * 1024 * 1024; // >48M 70 | 71 | if (DO_STATS || DO_MONITORS) { 72 | logInfo("ArrayCache.BUCKETS = " + BUCKETS); 73 | logInfo("ArrayCache.MIN_ARRAY_SIZE = " + MIN_ARRAY_SIZE); 74 | logInfo("ArrayCache.MAX_ARRAY_SIZE = " + MAX_ARRAY_SIZE); 75 | logInfo("ArrayCache.ARRAY_SIZES = " 76 | + Arrays.toString(ARRAY_SIZES)); 77 | logInfo("ArrayCache.THRESHOLD_ARRAY_SIZE = " 78 | + THRESHOLD_ARRAY_SIZE); 79 | logInfo("ArrayCache.THRESHOLD_HUGE_ARRAY_SIZE = " 80 | + THRESHOLD_HUGE_ARRAY_SIZE); 81 | } 82 | } 83 | 84 | private ArrayCacheConst() { 85 | // Utility class 86 | } 87 | 88 | // small methods used a lot (to be inlined / optimized by hotspot) 89 | 90 | static int getBucket(final int length) { 91 | for (int i = 0; i < ARRAY_SIZES.length; i++) { 92 | if (length <= ARRAY_SIZES[i]) { 93 | return i; 94 | } 95 | } 96 | return -1; 97 | } 98 | 99 | /** 100 | * Return the new array size (~ x2) 101 | * @param curSize current used size 102 | * @param needSize needed size 103 | * @return new array size 104 | */ 105 | public static int getNewSize(final int curSize, final int needSize) { 106 | // check if needSize is negative or integer overflow: 107 | if (needSize < 0) { 108 | // hard overflow failure - we can't even accommodate 109 | // new items without overflowing 110 | throw new ArrayIndexOutOfBoundsException( 111 | "array exceeds maximum capacity !"); 112 | } 113 | assert curSize >= 0; 114 | final int initial = curSize; 115 | int size; 116 | if (initial > THRESHOLD_ARRAY_SIZE) { 117 | size = initial + (initial >> 1); // x(3/2) 118 | } else { 119 | size = (initial << 1); // x2 120 | } 121 | // ensure the new size is >= needed size: 122 | if (size < needSize) { 123 | // align to 4096 (may overflow): 124 | size = ((needSize >> 12) + 1) << 12; 125 | } 126 | // check integer overflow: 127 | if (size < 0) { 128 | // resize to maximum capacity: 129 | size = Integer.MAX_VALUE; 130 | } 131 | return size; 132 | } 133 | 134 | /** 135 | * Return the new array size (~ x2) 136 | * @param curSize current used size 137 | * @param needSize needed size 138 | * @return new array size 139 | */ 140 | public static long getNewLargeSize(final long curSize, final long needSize) { 141 | // check if needSize is negative or integer overflow: 142 | if ((needSize >> 31L) != 0L) { 143 | // hard overflow failure - we can't even accommodate 144 | // new items without overflowing 145 | throw new ArrayIndexOutOfBoundsException( 146 | "array exceeds maximum capacity !"); 147 | } 148 | assert curSize >= 0L; 149 | long size; 150 | if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) { 151 | size = curSize + (curSize >> 2L); // x(5/4) 152 | } else if (curSize > THRESHOLD_ARRAY_SIZE) { 153 | size = curSize + (curSize >> 1L); // x(3/2) 154 | } else if (curSize > THRESHOLD_SMALL_ARRAY_SIZE) { 155 | size = (curSize << 1L); // x2 156 | } else { 157 | size = (curSize << 2L); // x4 158 | } 159 | // ensure the new size is >= needed size: 160 | if (size < needSize) { 161 | // align to 4096: 162 | size = ((needSize >> 12L) + 1L) << 12L; 163 | } 164 | // check integer overflow: 165 | if (size > Integer.MAX_VALUE) { 166 | // resize to maximum capacity: 167 | size = Integer.MAX_VALUE; 168 | } 169 | return size; 170 | } 171 | 172 | static final class CacheStats { 173 | final String name; 174 | final BucketStats[] bucketStats; 175 | int resize = 0; 176 | int oversize = 0; 177 | long totalInitial = 0L; 178 | 179 | CacheStats(final String name) { 180 | this.name = name; 181 | 182 | bucketStats = new BucketStats[BUCKETS]; 183 | for (int i = 0; i < BUCKETS; i++) { 184 | bucketStats[i] = new BucketStats(); 185 | } 186 | } 187 | 188 | void reset() { 189 | resize = 0; 190 | oversize = 0; 191 | 192 | for (int i = 0; i < BUCKETS; i++) { 193 | bucketStats[i].reset(); 194 | } 195 | } 196 | 197 | long dumpStats() { 198 | long totalCacheBytes = 0L; 199 | 200 | if (DO_STATS) { 201 | for (int i = 0; i < BUCKETS; i++) { 202 | final BucketStats s = bucketStats[i]; 203 | 204 | if (s.maxSize != 0) { 205 | totalCacheBytes += getByteFactor() 206 | * (s.maxSize * ARRAY_SIZES[i]); 207 | } 208 | } 209 | 210 | if (totalInitial != 0L || totalCacheBytes != 0L 211 | || resize != 0 || oversize != 0) 212 | { 213 | logInfo(name + ": resize: " + resize 214 | + " - oversize: " + oversize 215 | + " - initial: " + getTotalInitialBytes() 216 | + " bytes (" + totalInitial + " elements)" 217 | + " - cache: " + totalCacheBytes + " bytes" 218 | ); 219 | } 220 | 221 | if (totalCacheBytes != 0L) { 222 | logInfo(name + ": usage stats:"); 223 | 224 | for (int i = 0; i < BUCKETS; i++) { 225 | final BucketStats s = bucketStats[i]; 226 | 227 | if (s.getOp != 0) { 228 | logInfo(" Bucket[" + ARRAY_SIZES[i] + "]: " 229 | + "get: " + s.getOp 230 | + " - put: " + s.returnOp 231 | + " - create: " + s.createOp 232 | + " :: max size: " + s.maxSize 233 | ); 234 | } 235 | } 236 | } 237 | } 238 | return totalCacheBytes; 239 | } 240 | 241 | private int getByteFactor() { 242 | int factor = 1; 243 | if (name.contains("Int") || name.contains("Float")) { 244 | factor = 4; 245 | } else if (name.contains("Double")) { 246 | factor = 8; 247 | } 248 | return factor; 249 | } 250 | 251 | long getTotalInitialBytes() { 252 | return getByteFactor() * totalInitial; 253 | } 254 | } 255 | 256 | static final class BucketStats { 257 | int getOp = 0; 258 | int createOp = 0; 259 | int returnOp = 0; 260 | int maxSize = 0; 261 | 262 | void reset() { 263 | getOp = 0; 264 | createOp = 0; 265 | returnOp = 0; 266 | maxSize = 0; 267 | } 268 | 269 | void updateMaxSize(final int size) { 270 | if (size > maxSize) { 271 | maxSize = size; 272 | } 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/CollinearSimplifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import com.sun.javafx.geom.PathConsumer2D; 29 | 30 | public final class CollinearSimplifier implements PathConsumer2D { 31 | 32 | enum SimplifierState { 33 | 34 | Empty, PreviousPoint, PreviousLine 35 | }; 36 | // slope precision threshold 37 | static final float EPS = 1e-4f; // aaime proposed 1e-3f 38 | 39 | PathConsumer2D delegate; 40 | SimplifierState state; 41 | float px1, py1, px2, py2; 42 | float pslope; 43 | 44 | CollinearSimplifier() { 45 | } 46 | 47 | public CollinearSimplifier init(PathConsumer2D delegate) { 48 | this.delegate = delegate; 49 | this.state = SimplifierState.Empty; 50 | 51 | return this; // fluent API 52 | } 53 | 54 | @Override 55 | public void pathDone() { 56 | emitStashedLine(); 57 | state = SimplifierState.Empty; 58 | delegate.pathDone(); 59 | } 60 | 61 | @Override 62 | public void closePath() { 63 | emitStashedLine(); 64 | state = SimplifierState.Empty; 65 | delegate.closePath(); 66 | } 67 | 68 | @Override 69 | public void quadTo(float x1, float y1, float x2, float y2) { 70 | emitStashedLine(); 71 | delegate.quadTo(x1, y1, x2, y2); 72 | // final end point: 73 | state = SimplifierState.PreviousPoint; 74 | px1 = x2; 75 | py1 = y2; 76 | } 77 | 78 | @Override 79 | public void curveTo(float x1, float y1, float x2, float y2, 80 | float x3, float y3) { 81 | emitStashedLine(); 82 | delegate.curveTo(x1, y1, x2, y2, x3, y3); 83 | // final end point: 84 | state = SimplifierState.PreviousPoint; 85 | px1 = x3; 86 | py1 = y3; 87 | } 88 | 89 | @Override 90 | public void moveTo(float x, float y) { 91 | emitStashedLine(); 92 | delegate.moveTo(x, y); 93 | state = SimplifierState.PreviousPoint; 94 | px1 = x; 95 | py1 = y; 96 | } 97 | 98 | @Override 99 | public void lineTo(final float x, final float y) { 100 | switch (state) { 101 | case Empty: 102 | delegate.lineTo(x, y); 103 | state = SimplifierState.PreviousPoint; 104 | px1 = x; 105 | py1 = y; 106 | return; 107 | 108 | case PreviousPoint: 109 | state = SimplifierState.PreviousLine; 110 | px2 = x; 111 | py2 = y; 112 | pslope = getSlope(px1, py1, x, y); 113 | return; 114 | 115 | case PreviousLine: 116 | final float slope = getSlope(px2, py2, x, y); 117 | // test for collinearity 118 | if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) { 119 | // merge segments 120 | px2 = x; 121 | py2 = y; 122 | return; 123 | } 124 | // emit previous segment 125 | delegate.lineTo(px2, py2); 126 | px1 = px2; 127 | py1 = py2; 128 | px2 = x; 129 | py2 = y; 130 | pslope = slope; 131 | return; 132 | default: 133 | } 134 | } 135 | 136 | private void emitStashedLine() { 137 | if (state == SimplifierState.PreviousLine) { 138 | delegate.lineTo(px2, py2); 139 | } 140 | } 141 | 142 | private static float getSlope(float x1, float y1, float x2, float y2) { 143 | float dy = y2 - y1; 144 | if (dy == 0.0f) { 145 | return (x2 > x1) ? Float.POSITIVE_INFINITY 146 | : Float.NEGATIVE_INFINITY; 147 | } 148 | return (x2 - x1) / dy; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/DCollinearSimplifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | 29 | 30 | public final class DCollinearSimplifier implements DPathConsumer2D { 31 | 32 | enum SimplifierState { 33 | 34 | Empty, PreviousPoint, PreviousLine 35 | }; 36 | // slope precision threshold 37 | static final double EPS = 1e-4d; // aaime proposed 1e-3d 38 | 39 | DPathConsumer2D delegate; 40 | SimplifierState state; 41 | double px1, py1, px2, py2; 42 | double pslope; 43 | 44 | DCollinearSimplifier() { 45 | } 46 | 47 | public DCollinearSimplifier init(DPathConsumer2D delegate) { 48 | this.delegate = delegate; 49 | this.state = SimplifierState.Empty; 50 | 51 | return this; // fluent API 52 | } 53 | 54 | @Override 55 | public void pathDone() { 56 | emitStashedLine(); 57 | state = SimplifierState.Empty; 58 | delegate.pathDone(); 59 | } 60 | 61 | @Override 62 | public void closePath() { 63 | emitStashedLine(); 64 | state = SimplifierState.Empty; 65 | delegate.closePath(); 66 | } 67 | 68 | @Override 69 | public void quadTo(double x1, double y1, double x2, double y2) { 70 | emitStashedLine(); 71 | delegate.quadTo(x1, y1, x2, y2); 72 | // final end point: 73 | state = SimplifierState.PreviousPoint; 74 | px1 = x2; 75 | py1 = y2; 76 | } 77 | 78 | @Override 79 | public void curveTo(double x1, double y1, double x2, double y2, 80 | double x3, double y3) { 81 | emitStashedLine(); 82 | delegate.curveTo(x1, y1, x2, y2, x3, y3); 83 | // final end point: 84 | state = SimplifierState.PreviousPoint; 85 | px1 = x3; 86 | py1 = y3; 87 | } 88 | 89 | @Override 90 | public void moveTo(double x, double y) { 91 | emitStashedLine(); 92 | delegate.moveTo(x, y); 93 | state = SimplifierState.PreviousPoint; 94 | px1 = x; 95 | py1 = y; 96 | } 97 | 98 | @Override 99 | public void lineTo(final double x, final double y) { 100 | switch (state) { 101 | case Empty: 102 | delegate.lineTo(x, y); 103 | state = SimplifierState.PreviousPoint; 104 | px1 = x; 105 | py1 = y; 106 | return; 107 | 108 | case PreviousPoint: 109 | state = SimplifierState.PreviousLine; 110 | px2 = x; 111 | py2 = y; 112 | pslope = getSlope(px1, py1, x, y); 113 | return; 114 | 115 | case PreviousLine: 116 | final double slope = getSlope(px2, py2, x, y); 117 | // test for collinearity 118 | if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) { 119 | // merge segments 120 | px2 = x; 121 | py2 = y; 122 | return; 123 | } 124 | // emit previous segment 125 | delegate.lineTo(px2, py2); 126 | px1 = px2; 127 | py1 = py2; 128 | px2 = x; 129 | py2 = y; 130 | pslope = slope; 131 | return; 132 | default: 133 | } 134 | } 135 | 136 | private void emitStashedLine() { 137 | if (state == SimplifierState.PreviousLine) { 138 | delegate.lineTo(px2, py2); 139 | } 140 | } 141 | 142 | private static double getSlope(double x1, double y1, double x2, double y2) { 143 | double dy = y2 - y1; 144 | if (dy == 0.0d) { 145 | return (x2 > x1) ? Double.POSITIVE_INFINITY 146 | : Double.NEGATIVE_INFINITY; 147 | } 148 | return (x2 - x1) / dy; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/DMarlinRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | public interface DMarlinRenderer extends DPathConsumer2D { 29 | 30 | public DMarlinRenderer init(final int pix_boundsX, final int pix_boundsY, 31 | final int pix_boundsWidth, final int pix_boundsHeight, 32 | final int windingRule); 33 | 34 | /** 35 | * Disposes this renderer and recycle it clean up before reusing this instance 36 | */ 37 | public void dispose(); 38 | 39 | public int getOutpixMinX(); 40 | public int getOutpixMaxX(); 41 | public int getOutpixMinY(); 42 | public int getOutpixMaxY(); 43 | 44 | public void produceAlphas(MarlinAlphaConsumer ac); 45 | 46 | public double getOffsetX(); 47 | public double getOffsetY(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/DPathConsumer2D.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | public interface DPathConsumer2D { 29 | public void moveTo(double x0, double y0); 30 | public void lineTo(double x1, double y1); 31 | public void quadTo(double xc, double yc, 32 | double x1, double y1); 33 | public void curveTo(double xc0, double yc0, 34 | double xc1, double yc1, 35 | double x1, double y1); 36 | public void closePath(); 37 | public void pathDone(); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/DPathSimplifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | public final class DPathSimplifier implements DPathConsumer2D { 29 | 30 | // distance threshold in pixels (device) 31 | private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance(); 32 | // squared tolerance in pixels 33 | private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD; 34 | 35 | // members: 36 | private DPathConsumer2D delegate; 37 | // current reference point 38 | private double cx, cy; 39 | // flag indicating if the given point was skipped 40 | private boolean skipped; 41 | // last skipped point 42 | private double sx, sy; 43 | 44 | DPathSimplifier() { 45 | } 46 | 47 | public DPathSimplifier init(final DPathConsumer2D delegate) { 48 | this.delegate = delegate; 49 | skipped = false; 50 | return this; // fluent API 51 | } 52 | 53 | private void finishPath() { 54 | if (skipped) { 55 | _lineTo(sx, sy); 56 | } 57 | } 58 | 59 | @Override 60 | public void pathDone() { 61 | finishPath(); 62 | delegate.pathDone(); 63 | } 64 | 65 | @Override 66 | public void closePath() { 67 | finishPath(); 68 | delegate.closePath(); 69 | } 70 | 71 | @Override 72 | public void moveTo(final double xe, final double ye) { 73 | finishPath(); 74 | delegate.moveTo(xe, ye); 75 | cx = xe; 76 | cy = ye; 77 | } 78 | 79 | @Override 80 | public void lineTo(final double xe, final double ye) { 81 | // Test if segment is too small: 82 | double dx = (xe - cx); 83 | double dy = (ye - cy); 84 | 85 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 86 | skipped = true; 87 | sx = xe; 88 | sy = ye; 89 | return; 90 | } 91 | _lineTo(xe, ye); 92 | } 93 | 94 | private void _lineTo(final double xe, final double ye) { 95 | delegate.lineTo(xe, ye); 96 | cx = xe; 97 | cy = ye; 98 | skipped = false; 99 | } 100 | 101 | @Override 102 | public void quadTo(final double x1, final double y1, 103 | final double xe, final double ye) 104 | { 105 | // Test if curve is too small: 106 | double dx = (xe - cx); 107 | double dy = (ye - cy); 108 | 109 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 110 | // check control points P1: 111 | dx = (x1 - cx); 112 | dy = (y1 - cy); 113 | 114 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 115 | skipped = true; 116 | sx = xe; 117 | sy = ye; 118 | return; 119 | } 120 | } 121 | delegate.quadTo(x1, y1, xe, ye); 122 | cx = xe; 123 | cy = ye; 124 | skipped = false; 125 | } 126 | 127 | @Override 128 | public void curveTo(final double x1, final double y1, 129 | final double x2, final double y2, 130 | final double xe, final double ye) 131 | { 132 | // Test if curve is too small: 133 | double dx = (xe - cx); 134 | double dy = (ye - cy); 135 | 136 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 137 | // check control points P1: 138 | dx = (x1 - cx); 139 | dy = (y1 - cy); 140 | 141 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 142 | // check control points P2: 143 | dx = (x2 - cx); 144 | dy = (y2 - cy); 145 | 146 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 147 | skipped = true; 148 | sx = xe; 149 | sy = ye; 150 | return; 151 | } 152 | } 153 | } 154 | delegate.curveTo(x1, y1, x2, y2, xe, ye); 155 | cx = xe; 156 | cy = ye; 157 | skipped = false; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/FloatMath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | /** 29 | * Faster Math ceil / floor routines derived from StrictMath 30 | */ 31 | public final class FloatMath implements MarlinConst { 32 | 33 | // overflow / NaN handling enabled: 34 | static final boolean CHECK_OVERFLOW = true; 35 | static final boolean CHECK_NAN = true; 36 | 37 | private FloatMath() { 38 | // utility class 39 | } 40 | 41 | // faster inlined min/max functions in the branch prediction is high 42 | public static int max(final int a, final int b) { 43 | return (a >= b) ? a : b; 44 | } 45 | 46 | public static int min(final int a, final int b) { 47 | return (a <= b) ? a : b; 48 | } 49 | 50 | /** 51 | * Faster alternative to ceil(float) optimized for the integer domain 52 | * and supporting NaN and +/-Infinity. 53 | * 54 | * @param a a value. 55 | * @return the largest (closest to positive infinity) integer value 56 | * that less than or equal to the argument and is equal to a mathematical 57 | * integer. 58 | */ 59 | public static int ceil_int(final float a) { 60 | final int intpart = (int) a; 61 | 62 | if (a <= intpart 63 | || (CHECK_OVERFLOW && intpart == Integer.MAX_VALUE) 64 | || CHECK_NAN && Float.isNaN(a)) { 65 | return intpart; 66 | } 67 | return intpart + 1; 68 | } 69 | 70 | /** 71 | * Faster alternative to ceil(double) optimized for the integer domain 72 | * and supporting NaN and +/-Infinity. 73 | * 74 | * @param a a value. 75 | * @return the largest (closest to positive infinity) integer value 76 | * that less than or equal to the argument and is equal to a mathematical 77 | * integer. 78 | */ 79 | public static int ceil_int(final double a) { 80 | final int intpart = (int) a; 81 | 82 | if (a <= intpart 83 | || (CHECK_OVERFLOW && intpart == Integer.MAX_VALUE) 84 | || CHECK_NAN && Double.isNaN(a)) { 85 | return intpart; 86 | } 87 | return intpart + 1; 88 | } 89 | 90 | /** 91 | * Faster alternative to floor(float) optimized for the integer domain 92 | * and supporting NaN and +/-Infinity. 93 | * 94 | * @param a a value. 95 | * @return the largest (closest to positive infinity) floating-point value 96 | * that less than or equal to the argument and is equal to a mathematical 97 | * integer. 98 | */ 99 | public static int floor_int(final float a) { 100 | final int intpart = (int) a; 101 | 102 | if (a >= intpart 103 | || (CHECK_OVERFLOW && intpart == Integer.MIN_VALUE) 104 | || CHECK_NAN && Float.isNaN(a)) { 105 | return intpart; 106 | } 107 | return intpart - 1; 108 | } 109 | 110 | /** 111 | * Faster alternative to floor(double) optimized for the integer domain 112 | * and supporting NaN and +/-Infinity. 113 | * 114 | * @param a a value. 115 | * @return the largest (closest to positive infinity) floating-point value 116 | * that less than or equal to the argument and is equal to a mathematical 117 | * integer. 118 | */ 119 | public static int floor_int(final double a) { 120 | final int intpart = (int) a; 121 | 122 | if (a >= intpart 123 | || (CHECK_OVERFLOW && intpart == Integer.MIN_VALUE) 124 | || CHECK_NAN && Double.isNaN(a)) { 125 | return intpart; 126 | } 127 | return intpart - 1; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/MarlinAlphaConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import com.sun.openpisces.AlphaConsumer; 29 | 30 | public interface MarlinAlphaConsumer extends AlphaConsumer { 31 | 32 | public boolean supportBlockFlags(); 33 | 34 | public void clearAlphas(final int pix_y); 35 | 36 | public void setAndClearRelativeAlphas(int[] blkFlags, int alphaDeltas[], int pix_y, 37 | int firstdelta, int lastdelta); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/MarlinConst.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | /** 29 | * Marlin constant holder using System properties 30 | */ 31 | public interface MarlinConst { 32 | // enable Logs (logger or stdout) 33 | static final boolean ENABLE_LOGS = MarlinProperties.isLoggingEnabled(); 34 | // use Logger instead of stdout 35 | static final boolean USE_LOGGER = ENABLE_LOGS && MarlinProperties.isUseLogger(); 36 | 37 | // log new RendererContext 38 | static final boolean LOG_CREATE_CONTEXT = ENABLE_LOGS 39 | && MarlinProperties.isLogCreateContext(); 40 | // log misc.Unsafe alloc/realloc/free 41 | static final boolean LOG_UNSAFE_MALLOC = ENABLE_LOGS 42 | && MarlinProperties.isLogUnsafeMalloc(); 43 | // do check unsafe alignment: 44 | static final boolean DO_CHECK_UNSAFE = false; 45 | 46 | // do statistics 47 | static final boolean DO_STATS = ENABLE_LOGS && MarlinProperties.isDoStats(); 48 | // do monitors 49 | // disabled to reduce byte-code size a bit... 50 | static final boolean DO_MONITORS = false; 51 | // static final boolean DO_MONITORS = ENABLE_LOGS && MarlinProperties.isDoMonitors(); 52 | // do checks 53 | static final boolean DO_CHECKS = ENABLE_LOGS && MarlinProperties.isDoChecks(); 54 | 55 | // do AA range checks: disable when algorithm / code is stable 56 | static final boolean DO_AA_RANGE_CHECK = false; 57 | 58 | // enable logs 59 | static final boolean DO_LOG_WIDEN_ARRAY = ENABLE_LOGS && false; 60 | // enable oversize logs 61 | static final boolean DO_LOG_OVERSIZE = ENABLE_LOGS && false; 62 | // enable traces 63 | static final boolean DO_TRACE = ENABLE_LOGS && false; 64 | 65 | // do flush stats 66 | static final boolean DO_FLUSH_STATS = true; 67 | // do flush monitors 68 | static final boolean DO_FLUSH_MONITORS = true; 69 | // use one polling thread to dump statistics/monitors 70 | static final boolean USE_DUMP_THREAD = false; 71 | // thread dump interval (ms) 72 | static final long DUMP_INTERVAL = 5000L; 73 | 74 | // do clean dirty array 75 | static final boolean DO_CLEAN_DIRTY = false; 76 | 77 | // flag to use collinear simplifier 78 | static final boolean USE_SIMPLIFIER = MarlinProperties.isUseSimplifier(); 79 | 80 | // flag to use path simplifier 81 | static final boolean USE_PATH_SIMPLIFIER = MarlinProperties.isUsePathSimplifier(); 82 | 83 | static final boolean DO_CLIP_SUBDIVIDER = MarlinProperties.isDoClipSubdivider(); 84 | 85 | // flag to enable logs related to bounds checks 86 | static final boolean DO_LOG_BOUNDS = ENABLE_LOGS && false; 87 | 88 | // flag to enable logs related to clip rect 89 | static final boolean DO_LOG_CLIP = ENABLE_LOGS && false; 90 | 91 | // Initial Array sizing (initial context capacity) ~ 450K 92 | 93 | // 4096 pixels (width) for initial capacity 94 | static final int INITIAL_PIXEL_WIDTH 95 | = MarlinProperties.getInitialPixelWidth(); 96 | // 2176 pixels (height) for initial capacity 97 | static final int INITIAL_PIXEL_HEIGHT 98 | = MarlinProperties.getInitialPixelHeight(); 99 | 100 | // typical array sizes: only odd numbers allowed below 101 | static final int INITIAL_ARRAY = 256; 102 | 103 | // alpha row dimension 104 | static final int INITIAL_AA_ARRAY = INITIAL_PIXEL_WIDTH; 105 | 106 | // 4096 edges for initial capacity 107 | static final int INITIAL_EDGES_COUNT = MarlinProperties.getInitialEdges(); 108 | 109 | // initial edges = edges count (4096) 110 | // 6 ints per edges = 24 bytes 111 | // edges capacity = 24 x initial edges = 24 * edges count (4096) = 96K 112 | static final int INITIAL_EDGES_CAPACITY = INITIAL_EDGES_COUNT * 24; 113 | 114 | // crossing capacity = edges count / 4 ~ 1024 115 | static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2; 116 | 117 | // zero value as byte 118 | static final byte BYTE_0 = (byte) 0; 119 | 120 | // subpixels expressed as log2 121 | public static final int SUBPIXEL_LG_POSITIONS_X 122 | = MarlinProperties.getSubPixel_Log2_X(); 123 | public static final int SUBPIXEL_LG_POSITIONS_Y 124 | = MarlinProperties.getSubPixel_Log2_Y(); 125 | 126 | public static final int MIN_SUBPIXEL_LG_POSITIONS 127 | = Math.min(SUBPIXEL_LG_POSITIONS_X, SUBPIXEL_LG_POSITIONS_Y); 128 | 129 | // number of subpixels 130 | public static final int SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X); 131 | public static final int SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y); 132 | 133 | public static final float MIN_SUBPIXELS = 1 << MIN_SUBPIXEL_LG_POSITIONS; 134 | 135 | // 2176 pixels (height) x 8 subpixels = 68K 136 | static final int INITIAL_BUCKET_ARRAY 137 | = INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y; 138 | 139 | public static final int MAX_AA_ALPHA 140 | = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y); 141 | 142 | public static final int BLOCK_SIZE_LG = MarlinProperties.getBlockSize_Log2(); 143 | public static final int BLOCK_SIZE = 1 << BLOCK_SIZE_LG; 144 | 145 | static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags(); 146 | static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics(); 147 | 148 | static final boolean FORCE_RLE = MarlinProperties.isForceRLE(); 149 | static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE(); 150 | // minimum width to try using RLE encoding: 151 | static final int RLE_MIN_WIDTH 152 | = Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth()); 153 | 154 | // Constants 155 | public static final int WIND_EVEN_ODD = 0; 156 | public static final int WIND_NON_ZERO = 1; 157 | 158 | /** 159 | * Constant value for join style. 160 | */ 161 | public static final int JOIN_MITER = 0; 162 | 163 | /** 164 | * Constant value for join style. 165 | */ 166 | public static final int JOIN_ROUND = 1; 167 | 168 | /** 169 | * Constant value for join style. 170 | */ 171 | public static final int JOIN_BEVEL = 2; 172 | 173 | /** 174 | * Constant value for end cap style. 175 | */ 176 | public static final int CAP_BUTT = 0; 177 | 178 | /** 179 | * Constant value for end cap style. 180 | */ 181 | public static final int CAP_ROUND = 1; 182 | 183 | /** 184 | * Constant value for end cap style. 185 | */ 186 | public static final int CAP_SQUARE = 2; 187 | 188 | // Out codes 189 | static final int OUTCODE_TOP = 1; 190 | static final int OUTCODE_BOTTOM = 2; 191 | static final int OUTCODE_LEFT = 4; 192 | static final int OUTCODE_RIGHT = 8; 193 | static final int OUTCODE_MASK_T_B = OUTCODE_TOP | OUTCODE_BOTTOM; 194 | static final int OUTCODE_MASK_L_R = OUTCODE_LEFT | OUTCODE_RIGHT; 195 | static final int OUTCODE_MASK_T_B_L_R = OUTCODE_MASK_T_B | OUTCODE_MASK_L_R; 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/MarlinRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import com.sun.javafx.geom.PathConsumer2D; 29 | 30 | public interface MarlinRenderer extends PathConsumer2D { 31 | 32 | public MarlinRenderer init(final int pix_boundsX, final int pix_boundsY, 33 | final int pix_boundsWidth, final int pix_boundsHeight, 34 | final int windingRule); 35 | 36 | /** 37 | * Disposes this renderer and recycle it clean up before reusing this instance 38 | */ 39 | public void dispose(); 40 | 41 | public int getOutpixMinX(); 42 | public int getOutpixMaxX(); 43 | public int getOutpixMinY(); 44 | public int getOutpixMaxY(); 45 | 46 | public void produceAlphas(MarlinAlphaConsumer ac); 47 | 48 | public float getOffsetX(); 49 | public float getOffsetY(); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/MarlinUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | public final class MarlinUtils { 29 | // Marlin logger 30 | private static final java.util.logging.Logger LOG; 31 | 32 | static { 33 | if (MarlinConst.USE_LOGGER) { 34 | LOG = java.util.logging.Logger.getLogger("prism.marlin"); 35 | } else { 36 | LOG = null; 37 | } 38 | } 39 | 40 | private MarlinUtils() { 41 | // no-op 42 | } 43 | 44 | public static void logInfo(final String msg) { 45 | if (MarlinConst.USE_LOGGER) { 46 | LOG.info(msg); 47 | } else if (MarlinConst.ENABLE_LOGS) { 48 | System.out.print("INFO: "); 49 | System.out.println(msg); 50 | } 51 | } 52 | 53 | public static void logException(final String msg, final Throwable th) { 54 | if (MarlinConst.USE_LOGGER) { 55 | LOG.log(java.util.logging.Level.WARNING, msg, th); 56 | } else if (MarlinConst.ENABLE_LOGS) { 57 | System.out.print("WARNING: "); 58 | System.out.println(msg); 59 | th.printStackTrace(System.err); 60 | } 61 | } 62 | 63 | // From sun.awt.util.ThreadGroupUtils 64 | 65 | /** 66 | * Returns a root thread group. 67 | * Should be called with {@link sun.security.util.SecurityConstants#MODIFY_THREADGROUP_PERMISSION} 68 | * 69 | * @return a root {@code ThreadGroup} 70 | */ 71 | public static ThreadGroup getRootThreadGroup() { 72 | ThreadGroup currentTG = Thread.currentThread().getThreadGroup(); 73 | ThreadGroup parentTG = currentTG.getParent(); 74 | while (parentTG != null) { 75 | currentTG = parentTG; 76 | parentTG = currentTG.getParent(); 77 | } 78 | return currentTG; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/MergeSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | /** 29 | * MergeSort adapted from (OpenJDK 8) java.util.Array.legacyMergeSort(Object[]) 30 | * to swap two arrays at the same time (x & y) 31 | * and use external auxiliary storage for temporary arrays 32 | */ 33 | final class MergeSort { 34 | 35 | // insertion sort threshold 36 | public static final int INSERTION_SORT_THRESHOLD = 14; 37 | 38 | /** 39 | * Modified merge sort: 40 | * Input arrays are in both auxX/auxY (sorted: 0 to insertionSortIndex) 41 | * and x/y (unsorted: insertionSortIndex to toIndex) 42 | * Outputs are stored in x/y arrays 43 | */ 44 | static void mergeSortNoCopy(final int[] x, final int[] y, 45 | final int[] auxX, final int[] auxY, 46 | final int toIndex, 47 | final int insertionSortIndex) 48 | { 49 | if ((toIndex > x.length) || (toIndex > y.length) 50 | || (toIndex > auxX.length) || (toIndex > auxY.length)) { 51 | // explicit check to avoid bound checks within hot loops (below): 52 | throw new ArrayIndexOutOfBoundsException("bad arguments: toIndex=" 53 | + toIndex); 54 | } 55 | 56 | // sort second part only using merge / insertion sort 57 | // in auxiliary storage (auxX/auxY) 58 | mergeSort(x, y, x, auxX, y, auxY, insertionSortIndex, toIndex); 59 | 60 | // final pass to merge both 61 | // Merge sorted parts (auxX/auxY) into x/y arrays 62 | if ((insertionSortIndex == 0) 63 | || (auxX[insertionSortIndex - 1] <= auxX[insertionSortIndex])) { 64 | // 34 occurences 65 | // no initial left part or both sublists (auxX, auxY) are sorted: 66 | // copy back data into (x, y): 67 | System.arraycopy(auxX, 0, x, 0, toIndex); 68 | System.arraycopy(auxY, 0, y, 0, toIndex); 69 | return; 70 | } 71 | 72 | for (int i = 0, p = 0, q = insertionSortIndex; i < toIndex; i++) { 73 | if ((q >= toIndex) || ((p < insertionSortIndex) 74 | && (auxX[p] <= auxX[q]))) { 75 | x[i] = auxX[p]; 76 | y[i] = auxY[p]; 77 | p++; 78 | } else { 79 | x[i] = auxX[q]; 80 | y[i] = auxY[q]; 81 | q++; 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * Src is the source array that starts at index 0 88 | * Dest is the (possibly larger) array destination with a possible offset 89 | * low is the index in dest to start sorting 90 | * high is the end index in dest to end sorting 91 | */ 92 | private static void mergeSort(final int[] refX, final int[] refY, 93 | final int[] srcX, final int[] dstX, 94 | final int[] srcY, final int[] dstY, 95 | final int low, final int high) 96 | { 97 | final int length = high - low; 98 | 99 | /* 100 | * Tuning parameter: list size at or below which insertion sort 101 | * will be used in preference to mergesort. 102 | */ 103 | if (length <= INSERTION_SORT_THRESHOLD) { 104 | // Insertion sort on smallest arrays 105 | dstX[low] = refX[low]; 106 | dstY[low] = refY[low]; 107 | 108 | for (int i = low + 1, j = low, x, y; i < high; j = i++) { 109 | x = refX[i]; 110 | y = refY[i]; 111 | 112 | while (dstX[j] > x) { 113 | // swap element 114 | dstX[j + 1] = dstX[j]; 115 | dstY[j + 1] = dstY[j]; 116 | if (j-- == low) { 117 | break; 118 | } 119 | } 120 | dstX[j + 1] = x; 121 | dstY[j + 1] = y; 122 | } 123 | return; 124 | } 125 | 126 | // Recursively sort halves of dest into src 127 | 128 | // note: use signed shift (not >>>) for performance 129 | // as indices are small enough to exceed Integer.MAX_VALUE 130 | final int mid = (low + high) >> 1; 131 | 132 | mergeSort(refX, refY, dstX, srcX, dstY, srcY, low, mid); 133 | mergeSort(refX, refY, dstX, srcX, dstY, srcY, mid, high); 134 | 135 | // If arrays are inverted ie all(A) > all(B) do swap A and B to dst 136 | if (srcX[high - 1] <= srcX[low]) { 137 | // 1561 occurences 138 | final int left = mid - low; 139 | final int right = high - mid; 140 | final int off = (left != right) ? 1 : 0; 141 | // swap parts: 142 | System.arraycopy(srcX, low, dstX, mid + off, left); 143 | System.arraycopy(srcX, mid, dstX, low, right); 144 | System.arraycopy(srcY, low, dstY, mid + off, left); 145 | System.arraycopy(srcY, mid, dstY, low, right); 146 | return; 147 | } 148 | 149 | // If arrays are already sorted, just copy from src to dest. This is an 150 | // optimization that results in faster sorts for nearly ordered lists. 151 | if (srcX[mid - 1] <= srcX[mid]) { 152 | // 14 occurences 153 | System.arraycopy(srcX, low, dstX, low, length); 154 | System.arraycopy(srcY, low, dstY, low, length); 155 | return; 156 | } 157 | 158 | // Merge sorted halves (now in src) into dest 159 | for (int i = low, p = low, q = mid; i < high; i++) { 160 | if ((q >= high) || ((p < mid) && (srcX[p] <= srcX[q]))) { 161 | dstX[i] = srcX[p]; 162 | dstY[i] = srcY[p]; 163 | p++; 164 | } else { 165 | dstX[i] = srcX[q]; 166 | dstY[i] = srcY[q]; 167 | q++; 168 | } 169 | } 170 | } 171 | 172 | private MergeSort() { 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/OffHeapArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import java.lang.ref.PhantomReference; 29 | import java.lang.ref.ReferenceQueue; 30 | import java.lang.reflect.Field; 31 | import java.security.AccessController; 32 | import java.security.PrivilegedAction; 33 | import java.util.Vector; 34 | import static com.sun.marlin.MarlinConst.LOG_UNSAFE_MALLOC; 35 | import sun.misc.Unsafe; 36 | 37 | /** 38 | * 39 | */ 40 | final class OffHeapArray { 41 | 42 | // unsafe reference 43 | static final Unsafe UNSAFE; 44 | // size of int / float 45 | static final int SIZE_INT; 46 | 47 | static { 48 | UNSAFE = AccessController.doPrivileged(new PrivilegedAction() { 49 | @Override 50 | public Unsafe run() { 51 | Unsafe ref = null; 52 | try { 53 | final Field field = Unsafe.class.getDeclaredField("theUnsafe"); 54 | field.setAccessible(true); 55 | ref = (Unsafe) field.get(null); 56 | } catch (Exception e) { 57 | throw new InternalError("Unable to get sun.misc.Unsafe instance", e); 58 | } 59 | return ref; 60 | } 61 | }); 62 | 63 | SIZE_INT = Unsafe.ARRAY_INT_INDEX_SCALE; 64 | 65 | // Mimics Java2D Disposer: 66 | AccessController.doPrivileged( 67 | (PrivilegedAction) () -> { 68 | /* 69 | * The thread must be a member of a thread group 70 | * which will not get GCed before VM exit. 71 | * Make its parent the top-level thread group. 72 | */ 73 | final Thread t = new Thread( 74 | MarlinUtils.getRootThreadGroup(), 75 | new OffHeapDisposer(), 76 | "MarlinRenderer Disposer"); 77 | t.setContextClassLoader(null); 78 | t.setDaemon(true); 79 | t.setPriority(Thread.MAX_PRIORITY - 2); 80 | t.start(); 81 | return null; 82 | } 83 | ); 84 | } 85 | 86 | /* members */ 87 | long address; 88 | long length; 89 | int used; 90 | 91 | OffHeapArray(final Object parent, final long len) { 92 | // note: may throw OOME: 93 | this.address = UNSAFE.allocateMemory(len); 94 | this.length = len; 95 | this.used = 0; 96 | if (LOG_UNSAFE_MALLOC) { 97 | MarlinUtils.logInfo(System.currentTimeMillis() 98 | + ": OffHeapArray.allocateMemory = " 99 | + len + " to addr = " + this.address); 100 | } 101 | 102 | // Create the phantom reference to ensure freeing off-heap memory: 103 | REF_LIST.add(new OffHeapReference(parent, this)); 104 | } 105 | 106 | /* 107 | * As realloc may change the address, updating address is MANDATORY 108 | * @param len new array length 109 | * @throws OutOfMemoryError if the allocation is refused by the system 110 | */ 111 | void resize(final long len) { 112 | // note: may throw OOME: 113 | this.address = UNSAFE.reallocateMemory(address, len); 114 | this.length = len; 115 | if (LOG_UNSAFE_MALLOC) { 116 | MarlinUtils.logInfo(System.currentTimeMillis() 117 | + ": OffHeapArray.reallocateMemory = " 118 | + len + " to addr = " + this.address); 119 | } 120 | } 121 | 122 | void free() { 123 | UNSAFE.freeMemory(this.address); 124 | if (LOG_UNSAFE_MALLOC) { 125 | MarlinUtils.logInfo(System.currentTimeMillis() 126 | + ": OffHeapArray.freeMemory = " 127 | + this.length 128 | + " at addr = " + this.address); 129 | } 130 | this.address = 0L; 131 | } 132 | 133 | void fill(final byte val) { 134 | UNSAFE.setMemory(this.address, this.length, val); 135 | } 136 | 137 | // Custom disposer (replaced by jdk9 Cleaner) 138 | 139 | // Parent reference queue 140 | private static final ReferenceQueue REF_QUEUE 141 | = new ReferenceQueue(); 142 | // reference list 143 | private static final Vector REF_LIST 144 | = new Vector(32); 145 | 146 | static final class OffHeapReference extends PhantomReference { 147 | 148 | private final OffHeapArray array; 149 | 150 | OffHeapReference(final Object parent, final OffHeapArray edges) { 151 | super(parent, REF_QUEUE); 152 | this.array = edges; 153 | } 154 | 155 | void dispose() { 156 | // free off-heap blocks 157 | this.array.free(); 158 | } 159 | } 160 | 161 | static final class OffHeapDisposer implements Runnable { 162 | @Override 163 | public void run() { 164 | final Thread currentThread = Thread.currentThread(); 165 | OffHeapReference ref; 166 | 167 | // check interrupted: 168 | for (; !currentThread.isInterrupted();) { 169 | try { 170 | ref = (OffHeapReference)REF_QUEUE.remove(); 171 | ref.dispose(); 172 | 173 | REF_LIST.remove(ref); 174 | 175 | } catch (InterruptedException ie) { 176 | MarlinUtils.logException("OffHeapDisposer interrupted:", 177 | ie); 178 | } 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/PathSimplifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import com.sun.javafx.geom.PathConsumer2D; 29 | 30 | public final class PathSimplifier implements PathConsumer2D { 31 | 32 | // distance threshold in pixels (device) 33 | private static final float PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance(); 34 | // squared tolerance in pixels 35 | private static final float SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD; 36 | 37 | // members: 38 | private PathConsumer2D delegate; 39 | // current reference point 40 | private float cx, cy; 41 | // flag indicating if the given point was skipped 42 | private boolean skipped; 43 | // last skipped point 44 | private float sx, sy; 45 | 46 | PathSimplifier() { 47 | } 48 | 49 | public PathSimplifier init(final PathConsumer2D delegate) { 50 | this.delegate = delegate; 51 | skipped = false; 52 | return this; // fluent API 53 | } 54 | 55 | private void finishPath() { 56 | if (skipped) { 57 | _lineTo(sx, sy); 58 | } 59 | } 60 | 61 | @Override 62 | public void pathDone() { 63 | finishPath(); 64 | delegate.pathDone(); 65 | } 66 | 67 | @Override 68 | public void closePath() { 69 | finishPath(); 70 | delegate.closePath(); 71 | } 72 | 73 | @Override 74 | public void moveTo(final float xe, final float ye) { 75 | finishPath(); 76 | delegate.moveTo(xe, ye); 77 | cx = xe; 78 | cy = ye; 79 | } 80 | 81 | @Override 82 | public void lineTo(final float xe, final float ye) { 83 | // Test if segment is too small: 84 | float dx = (xe - cx); 85 | float dy = (ye - cy); 86 | 87 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 88 | skipped = true; 89 | sx = xe; 90 | sy = ye; 91 | return; 92 | } 93 | _lineTo(xe, ye); 94 | } 95 | 96 | private void _lineTo(final float xe, final float ye) { 97 | delegate.lineTo(xe, ye); 98 | cx = xe; 99 | cy = ye; 100 | skipped = false; 101 | } 102 | 103 | @Override 104 | public void quadTo(final float x1, final float y1, 105 | final float xe, final float ye) 106 | { 107 | // Test if curve is too small: 108 | float dx = (xe - cx); 109 | float dy = (ye - cy); 110 | 111 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 112 | // check control points P1: 113 | dx = (x1 - cx); 114 | dy = (y1 - cy); 115 | 116 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 117 | skipped = true; 118 | sx = xe; 119 | sy = ye; 120 | return; 121 | } 122 | } 123 | delegate.quadTo(x1, y1, xe, ye); 124 | cx = xe; 125 | cy = ye; 126 | skipped = false; 127 | } 128 | 129 | @Override 130 | public void curveTo(final float x1, final float y1, 131 | final float x2, final float y2, 132 | final float xe, final float ye) 133 | { 134 | // Test if curve is too small: 135 | float dx = (xe - cx); 136 | float dy = (ye - cy); 137 | 138 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 139 | // check control points P1: 140 | dx = (x1 - cx); 141 | dy = (y1 - cy); 142 | 143 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 144 | // check control points P2: 145 | dx = (x2 - cx); 146 | dy = (y2 - cy); 147 | 148 | if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { 149 | skipped = true; 150 | sx = xe; 151 | sy = ye; 152 | return; 153 | } 154 | } 155 | } 156 | delegate.curveTo(x1, y1, x2, y2, xe, ye); 157 | cx = xe; 158 | cy = ye; 159 | skipped = false; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin; 27 | 28 | import java.io.InputStream; 29 | import java.util.Properties; 30 | 31 | public final class Version { 32 | 33 | private static String version = null; 34 | 35 | public static String getVersion() { 36 | if (version == null) { 37 | version="undefined"; 38 | /* load Version.properties */ 39 | try { 40 | InputStream in = Version.class.getResourceAsStream("Version.properties"); 41 | Properties prop = new Properties(); 42 | prop.load(in); 43 | 44 | version = prop.getProperty("version", version); 45 | in.close(); 46 | } catch (Exception e) {} 47 | } 48 | return version; 49 | } 50 | 51 | private Version() {} 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/conv.sh: -------------------------------------------------------------------------------- 1 | FILES="CollinearSimplifier Curve Renderer RendererNoAA Stroker TransformingPathConsumer2D" 2 | # MarlinRenderingEngine 3 | for f in $FILES 4 | do 5 | echo "Processing $f" 6 | sed -e "s/$f/D$f/g" -e "s/\"D$f/\"$f/g" -e 's/import com.sun.javafx.geom.PathConsumer2D;//g' -e 's/PathConsumer2D/DPathConsumer2D/g' -e 's/DTransformingDPathConsumer2D/DTransformingPathConsumer2D/g' -e 's/(float) //g' -e 's/float/double/g' -e 's/Float/Double/g' -e 's/DoubleMath/FloatMath/g' -e 's/\([0-9]*\.\?[0-9]\+\)f/\1d/g' -e 's/ Curve/ DCurve/g' -e 's/Helpers/DHelpers/g' -e 's/MarlinRenderer/DMarlinRenderer/g' -e 's/RendererContext/DRendererContext/g' -e "s/DD$f/D$f/g" -e 's/MarlinDRenderer/DMarlinRenderer/g' -e 's/doubleing/floating/g' < $f.java > D$f.java 7 | done 8 | 9 | echo "Processing Renderers (final)" 10 | FILES="Renderer RendererNoAA" 11 | for f in $FILES 12 | do 13 | echo "Processing D$f" 14 | mv D$f.java D$f.java.orig 15 | sed -e "s/x1d/x1/g" -e "s/y1d/y1/g" < D$f.java.orig > D$f.java 16 | rm D$f.java.orig 17 | done 18 | 19 | 20 | # Dasher (convert type) 21 | echo "Processing Dasher" 22 | sed -e 's/Dasher/DDasher/g' -e 's/import com.sun.javafx.geom.PathConsumer2D;//g' -e 's/PathConsumer2D/DPathConsumer2D/g' -e 's/DTransformingDPathConsumer2D/DTransformingPathConsumer2D/g' -e 's/(float) //g' -e 's/float/double/g' -e 's/Float/Double/g' -e 's/DoubleMath/FloatMath/g' -e 's/\([0-9]*\.\?[0-9]\+\)f/\1d/g' -e 's/ Curve/ DCurve/g' -e 's/Helpers/DHelpers/g' -e 's/MarlinRenderer/DMarlinRenderer/g' -e 's/RendererContext/DRendererContext/g' -e 's/MarlinDRenderer/DMarlinRenderer/g' -e 's/copyDashArray(final double\[\] dashes)/copyDashArray(final float\[\] dashes)/g' -e 's/System.arraycopy(dashes, 0, newDashes, 0, len);/for \(int i = 0; i < len; i\+\+\) \{ newDashes\[i\] = dashes\[i\]; \}/g'< Dasher.java > DDasher.java 23 | 24 | echo "Processing Helpers" 25 | sed -e 's/import com.sun.javafx.geom.PathConsumer2D;//g' -e 's/PathConsumer2D/DPathConsumer2D/g' -e 's/DTransformingDPathConsumer2D/DTransformingPathConsumer2D/g' -e 's/(float) //g' -e 's/float/double/g' -e 's/Float/Double/g' -e 's/DoubleMath/FloatMath/g' -e 's/\([0-9]*\.\?[0-9]\+\)f/\1d/g' -e 's/ Curve/ DCurve/g' -e 's/Helpers/DHelpers/g' -e 's/MarlinRenderer/DMarlinRenderer/g' -e 's/RendererContext/DRendererContext/g' -e 's/MarlinDRenderer/DMarlinRenderer/g' < Helpers.java > DHelpers.java 26 | 27 | # [( -+]+[0-9]+f matches all integer float like 0f 123f (missing .0) 28 | # [0-9]+f matches all floats without .xx 29 | # \.[0-9]+[ /\*\+\-\(\)] matches all missing double of float suffix 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/makeIntFloatClasses.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Replicate changes from the ByteArrayCache class to the [Int/Float/Double]ArrayCache classes 3 | 4 | sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java 5 | sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java 6 | sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/stats/Histogram.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin.stats; 27 | 28 | /** 29 | * Generic histogram based on long statistics 30 | */ 31 | public final class Histogram extends StatLong { 32 | 33 | static final int BUCKET = 2; 34 | static final int MAX = 20; 35 | static final int LAST = MAX - 1; 36 | static final int[] STEPS = new int[MAX]; 37 | 38 | static { 39 | STEPS[0] = 0; 40 | STEPS[1] = 1; 41 | 42 | for (int i = 2; i < MAX; i++) { 43 | STEPS[i] = STEPS[i - 1] * BUCKET; 44 | } 45 | } 46 | 47 | static int bucket(int val) { 48 | for (int i = 1; i < MAX; i++) { 49 | if (val < STEPS[i]) { 50 | return i - 1; 51 | } 52 | } 53 | return LAST; 54 | } 55 | 56 | private final StatLong[] stats = new StatLong[MAX]; 57 | 58 | public Histogram(final String name) { 59 | super(name); 60 | for (int i = 0; i < MAX; i++) { 61 | stats[i] = new StatLong(String.format("%5s .. %5s", STEPS[i], 62 | ((i + 1 < MAX) ? STEPS[i + 1] : "~"))); 63 | } 64 | } 65 | 66 | @Override 67 | public void reset() { 68 | super.reset(); 69 | for (int i = 0; i < MAX; i++) { 70 | stats[i].reset(); 71 | } 72 | } 73 | 74 | @Override 75 | public void add(int val) { 76 | super.add(val); 77 | stats[bucket(val)].add(val); 78 | } 79 | 80 | @Override 81 | public void add(long val) { 82 | add((int) val); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | final StringBuilder sb = new StringBuilder(2048); 88 | super.toString(sb).append(" { "); 89 | 90 | for (int i = 0; i < MAX; i++) { 91 | if (stats[i].count != 0l) { 92 | sb.append("\n ").append(stats[i].toString()); 93 | } 94 | } 95 | 96 | return sb.append(" }").toString(); 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/stats/Monitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin.stats; 27 | 28 | /** 29 | * Generic monitor ie gathers time statistics as nanos. 30 | */ 31 | public final class Monitor extends StatLong { 32 | 33 | private static final long INVALID = -1L; 34 | 35 | private long start = INVALID; 36 | 37 | public Monitor(final String name) { 38 | super(name); 39 | } 40 | 41 | public void start() { 42 | start = System.nanoTime(); 43 | } 44 | 45 | public void stop() { 46 | final long elapsed = System.nanoTime() - start; 47 | if (start != INVALID && elapsed > 0l) { 48 | add(elapsed); 49 | } 50 | start = INVALID; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/sun/marlin/stats/StatLong.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.marlin.stats; 27 | 28 | /** 29 | * Statistics as long values 30 | */ 31 | public class StatLong { 32 | 33 | public final String name; 34 | public long count = 0l; 35 | public long sum = 0l; 36 | public long min = Integer.MAX_VALUE; 37 | public long max = Integer.MIN_VALUE; 38 | 39 | public StatLong(final String name) { 40 | this.name = name; 41 | } 42 | 43 | public void reset() { 44 | count = 0l; 45 | sum = 0l; 46 | min = Integer.MAX_VALUE; 47 | max = Integer.MIN_VALUE; 48 | } 49 | 50 | public void add(final int val) { 51 | count++; 52 | sum += val; 53 | if (val < min) { 54 | min = val; 55 | } 56 | if (val > max) { 57 | max = val; 58 | } 59 | } 60 | 61 | public void add(final long val) { 62 | count++; 63 | sum += val; 64 | if (val < min) { 65 | min = val; 66 | } 67 | if (val > max) { 68 | max = val; 69 | } 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return toString(new StringBuilder(128)).toString(); 75 | } 76 | 77 | public final StringBuilder toString(final StringBuilder sb) { 78 | sb.append(name).append('[').append(count); 79 | sb.append("] sum: ").append(sum).append(" avg: "); 80 | sb.append(trimTo3Digits(((double) sum) / count)); 81 | sb.append(" [").append(min).append(" | ").append(max).append("]"); 82 | return sb; 83 | } 84 | 85 | /** 86 | * Adjust the given double value to keep only 3 decimal digits 87 | * 88 | * @param value value to adjust 89 | * @return double value with only 3 decimal digits 90 | */ 91 | public static double trimTo3Digits(final double value) { 92 | return ((long) (1e3d * value)) / 1e3d; 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /src/main/java/com/sun/prism/impl/shape/DMarlinRasterizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.prism.impl.shape; 27 | 28 | import com.sun.javafx.geom.Path2D; 29 | import com.sun.javafx.geom.RectBounds; 30 | import com.sun.javafx.geom.Rectangle; 31 | import com.sun.javafx.geom.Shape; 32 | import com.sun.javafx.geom.transform.BaseTransform; 33 | import com.sun.javafx.sg.prism.NGCanvasPath; 34 | import com.sun.marlin.DMarlinRenderer; 35 | import com.sun.marlin.DMarlinRenderingEngine; 36 | import com.sun.marlin.MaskMarlinAlphaConsumer; 37 | import com.sun.marlin.DRendererContext; 38 | import com.sun.prism.BasicStroke; 39 | import com.sun.prism.impl.PrismSettings; 40 | 41 | /** 42 | * Thread-safe Marlin rasterizer (TL or CLQ storage) 43 | */ 44 | public final class DMarlinRasterizer implements ShapeRasterizer { 45 | private static final MaskData EMPTY_MASK = MaskData.create(new byte[1], 0, 0, 1, 1); 46 | 47 | @Override 48 | public MaskData getMaskData(Shape shape, 49 | BasicStroke stroke, 50 | RectBounds xformBounds, 51 | BaseTransform xform, 52 | boolean close, boolean antialiasedShape) 53 | { 54 | if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) { 55 | // RT-27427 56 | // TODO: Optimize the combinatorial strokes for simple 57 | // shapes and/or teach the rasterizer to be able to 58 | // do a "differential fill" between two shapes. 59 | // Note that most simple shapes will use a more optimized path 60 | // than this method for the INNER/OUTER strokes anyway. 61 | shape = stroke.createStrokedShape(shape); 62 | stroke = null; 63 | } 64 | if (xformBounds == null) { 65 | if (stroke != null) { 66 | // Note that all places that pass null for xformbounds also 67 | // pass null for stroke so that the following is not typically 68 | // executed, but just here as a safety net. 69 | shape = stroke.createStrokedShape(shape); 70 | stroke = null; 71 | } 72 | 73 | xformBounds = new RectBounds(); 74 | //TODO: Need to verify that this is a safe cast ... (RT-27427) 75 | xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds); 76 | } 77 | if (xformBounds.isEmpty()) { 78 | return EMPTY_MASK; 79 | } 80 | 81 | final DRendererContext rdrCtx = DMarlinRenderingEngine.getRendererContext(); 82 | DMarlinRenderer renderer = null; 83 | try { 84 | final Rectangle rclip = rdrCtx.clip; 85 | rclip.setBounds(xformBounds); 86 | 87 | if (shape instanceof NGCanvasPath) { 88 | final NGCanvasPath path = (NGCanvasPath)shape; 89 | shape = path.getGeometry(); // use internal Path2D 90 | // adjust xform: 91 | xform = path.getCombinedTransform(xform); 92 | } 93 | renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, 94 | antialiasedShape); 95 | 96 | final int outpix_xmin = renderer.getOutpixMinX(); 97 | final int outpix_xmax = renderer.getOutpixMaxX(); 98 | final int outpix_ymin = renderer.getOutpixMinY(); 99 | final int outpix_ymax = renderer.getOutpixMaxY(); 100 | final int w = outpix_xmax - outpix_xmin; 101 | final int h = outpix_ymax - outpix_ymin; 102 | if ((w <= 0) || (h <= 0)) { 103 | return EMPTY_MASK; 104 | } 105 | 106 | MaskMarlinAlphaConsumer consumer = rdrCtx.consumer; 107 | if (consumer == null || (w * h) > consumer.getAlphaLength()) { 108 | final int csize = (w * h + 0xfff) & (~0xfff); 109 | // TODO: use larger (1/16th more memory) + 4K page alignment ? 110 | rdrCtx.consumer = consumer = new MaskMarlinAlphaConsumer(csize); 111 | if (PrismSettings.verbose) { 112 | System.out.println("new alphas with length = " + csize); 113 | } 114 | } 115 | consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h); 116 | renderer.produceAlphas(consumer); 117 | 118 | return consumer.getMaskData(); 119 | } finally { 120 | if (renderer != null) { 121 | renderer.dispose(); 122 | } 123 | // recycle the DRendererContext instance 124 | DMarlinRenderingEngine.returnRendererContext(rdrCtx); 125 | } 126 | } 127 | 128 | static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) 129 | { 130 | final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? 131 | stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; 132 | 133 | final DRendererContext rdrCtx = DMarlinRenderingEngine.getRendererContext(); 134 | try { 135 | // initialize a large copyable Path2D to avoid a lot of array growing: 136 | final Path2D p2d = rdrCtx.getPath2D(); 137 | 138 | if (s instanceof NGCanvasPath) { 139 | s = ((NGCanvasPath)s).getGeometry(); // use internal Path2D 140 | } 141 | DMarlinPrismUtils.strokeTo(rdrCtx, s, stroke, lw, 142 | rdrCtx.transformerPC2D.wrapPath2D(p2d) 143 | ); 144 | 145 | // Use Path2D copy constructor (trim) 146 | return new Path2D(p2d); 147 | 148 | } finally { 149 | // recycle the DRendererContext instance 150 | DMarlinRenderingEngine.returnRendererContext(rdrCtx); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/sun/prism/impl/shape/MarlinRasterizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.prism.impl.shape; 27 | 28 | import com.sun.javafx.geom.Path2D; 29 | import com.sun.javafx.geom.RectBounds; 30 | import com.sun.javafx.geom.Rectangle; 31 | import com.sun.javafx.geom.Shape; 32 | import com.sun.javafx.geom.transform.BaseTransform; 33 | import com.sun.javafx.sg.prism.NGCanvasPath; 34 | import com.sun.marlin.MarlinRenderer; 35 | import com.sun.marlin.MarlinRenderingEngine; 36 | import com.sun.marlin.MaskMarlinAlphaConsumer; 37 | import com.sun.marlin.RendererContext; 38 | import com.sun.prism.BasicStroke; 39 | import com.sun.prism.impl.PrismSettings; 40 | 41 | /** 42 | * Thread-safe Marlin rasterizer (TL or CLQ storage) 43 | */ 44 | public final class MarlinRasterizer implements ShapeRasterizer { 45 | private static final MaskData EMPTY_MASK = MaskData.create(new byte[1], 0, 0, 1, 1); 46 | 47 | @Override 48 | public MaskData getMaskData(Shape shape, 49 | BasicStroke stroke, 50 | RectBounds xformBounds, 51 | BaseTransform xform, 52 | boolean close, boolean antialiasedShape) 53 | { 54 | if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) { 55 | // RT-27427 56 | // TODO: Optimize the combinatorial strokes for simple 57 | // shapes and/or teach the rasterizer to be able to 58 | // do a "differential fill" between two shapes. 59 | // Note that most simple shapes will use a more optimized path 60 | // than this method for the INNER/OUTER strokes anyway. 61 | shape = stroke.createStrokedShape(shape); 62 | stroke = null; 63 | } 64 | if (xformBounds == null) { 65 | if (stroke != null) { 66 | // Note that all places that pass null for xformbounds also 67 | // pass null for stroke so that the following is not typically 68 | // executed, but just here as a safety net. 69 | shape = stroke.createStrokedShape(shape); 70 | stroke = null; 71 | } 72 | 73 | xformBounds = new RectBounds(); 74 | //TODO: Need to verify that this is a safe cast ... (RT-27427) 75 | xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds); 76 | } 77 | if (xformBounds.isEmpty()) { 78 | return EMPTY_MASK; 79 | } 80 | 81 | final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext(); 82 | MarlinRenderer renderer = null; 83 | try { 84 | final Rectangle rclip = rdrCtx.clip; 85 | rclip.setBounds(xformBounds); 86 | 87 | if (shape instanceof NGCanvasPath) { 88 | final NGCanvasPath path = (NGCanvasPath)shape; 89 | shape = path.getGeometry(); // use internal Path2D 90 | // adjust xform: 91 | xform = path.getCombinedTransform(xform); 92 | } 93 | renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, 94 | antialiasedShape); 95 | 96 | final int outpix_xmin = renderer.getOutpixMinX(); 97 | final int outpix_xmax = renderer.getOutpixMaxX(); 98 | final int outpix_ymin = renderer.getOutpixMinY(); 99 | final int outpix_ymax = renderer.getOutpixMaxY(); 100 | final int w = outpix_xmax - outpix_xmin; 101 | final int h = outpix_ymax - outpix_ymin; 102 | if ((w <= 0) || (h <= 0)) { 103 | return EMPTY_MASK; 104 | } 105 | 106 | MaskMarlinAlphaConsumer consumer = rdrCtx.consumer; 107 | if (consumer == null || (w * h) > consumer.getAlphaLength()) { 108 | final int csize = (w * h + 0xfff) & (~0xfff); 109 | rdrCtx.consumer = consumer = new MaskMarlinAlphaConsumer(csize); 110 | if (PrismSettings.verbose) { 111 | System.out.println("new alphas with length = " + csize); 112 | } 113 | } 114 | consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h); 115 | renderer.produceAlphas(consumer); 116 | 117 | return consumer.getMaskData(); 118 | } finally { 119 | if (renderer != null) { 120 | renderer.dispose(); 121 | } 122 | // recycle the RendererContext instance 123 | MarlinRenderingEngine.returnRendererContext(rdrCtx); 124 | } 125 | } 126 | 127 | static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) 128 | { 129 | final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? 130 | stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; 131 | 132 | final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext(); 133 | try { 134 | // initialize a large copyable Path2D to avoid a lot of array growing: 135 | final Path2D p2d = rdrCtx.getPath2D(); 136 | 137 | if (s instanceof NGCanvasPath) { 138 | s = ((NGCanvasPath)s).getGeometry(); // use internal Path2D 139 | } 140 | MarlinPrismUtils.strokeTo(rdrCtx, s, stroke, lw, 141 | rdrCtx.transformerPC2D.wrapPath2D(p2d) 142 | ); 143 | 144 | // Use Path2D copy constructor (trim) 145 | return new Path2D(p2d); 146 | 147 | } finally { 148 | // recycle the RendererContext instance 149 | MarlinRenderingEngine.returnRendererContext(rdrCtx); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/sun/prism/impl/shape/ShapeUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.prism.impl.shape; 27 | 28 | import com.sun.javafx.geom.Path2D; 29 | import com.sun.javafx.geom.PathConsumer2D; 30 | import com.sun.javafx.geom.RectBounds; 31 | import com.sun.javafx.geom.Shape; 32 | import com.sun.javafx.geom.transform.BaseTransform; 33 | import com.sun.prism.BasicStroke; 34 | import com.sun.prism.impl.PrismSettings; 35 | 36 | public class ShapeUtil { 37 | 38 | private static final boolean MARLIN_ENABLED; 39 | private static final boolean USE_MARLIN_DP; 40 | private static final ShapeRasterizer shapeRasterizer; 41 | static { 42 | // Enable Marlin-FX by setting -Dprism.marlin=true 43 | MARLIN_ENABLED = com.sun.marlin.MarlinProperties.isMarlinEnabled(); 44 | USE_MARLIN_DP = com.sun.marlin.MarlinProperties.isDoublePrecisionEnabled(); 45 | 46 | if (MARLIN_ENABLED) { 47 | if (USE_MARLIN_DP) { 48 | System.out.println("Marlin-FX[" + com.sun.marlin.Version.getVersion() + "] (double) enabled."); 49 | shapeRasterizer = new DMarlinRasterizer(); 50 | } else { 51 | System.out.println("Marlin-FX[" + com.sun.marlin.Version.getVersion() + "] (float) enabled."); 52 | shapeRasterizer = new MarlinRasterizer(); 53 | } 54 | } else if (PrismSettings.doNativePisces) { 55 | shapeRasterizer = new NativePiscesRasterizer(); 56 | } else { 57 | shapeRasterizer = new OpenPiscesRasterizer(); 58 | } 59 | } 60 | 61 | public static MaskData rasterizeShape(Shape shape, 62 | BasicStroke stroke, 63 | RectBounds xformBounds, 64 | BaseTransform xform, 65 | boolean close, boolean antialiasedShape) 66 | { 67 | return shapeRasterizer.getMaskData(shape, stroke, xformBounds, xform, close, antialiasedShape); 68 | } 69 | 70 | public static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) 71 | { 72 | if (MARLIN_ENABLED) { 73 | if (USE_MARLIN_DP) { 74 | return DMarlinRasterizer.createCenteredStrokedShape(s, stroke); 75 | } 76 | return MarlinRasterizer.createCenteredStrokedShape(s, stroke); 77 | } 78 | // JavaPisces fallback: 79 | return createCenteredStrokedShapeOpenPisces(s, stroke); 80 | } 81 | 82 | private static Shape createCenteredStrokedShapeOpenPisces(Shape s, BasicStroke stroke) 83 | { 84 | final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? 85 | stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; 86 | 87 | final Path2D p2d = new Path2D(Path2D.WIND_NON_ZERO); 88 | 89 | PathConsumer2D pc2d = 90 | new com.sun.openpisces.Stroker(p2d, lw, stroke.getEndCap(), 91 | stroke.getLineJoin(), 92 | stroke.getMiterLimit()); 93 | 94 | if (stroke.isDashed()) { 95 | pc2d = new com.sun.openpisces.Dasher(pc2d, stroke.getDashArray(), 96 | stroke.getDashPhase()); 97 | } 98 | com.sun.prism.impl.shape.OpenPiscesPrismUtils.feedConsumer( 99 | s.getPathIterator(null), pc2d); 100 | 101 | return p2d; 102 | } 103 | 104 | /** 105 | * Private constructor to prevent instantiation. 106 | */ 107 | private ShapeUtil() { 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/sun/util/reentrant/ReentrantContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.util.reentrant; 27 | 28 | import java.lang.ref.Reference; 29 | 30 | /** 31 | * ReentrantContext is a base class to hold thread-local data supporting 32 | * reentrancy in either a ThreadLocal or a ConcurrentLinkedQueue 33 | * 34 | * @see ReentrantContextProvider 35 | */ 36 | public class ReentrantContext { 37 | // usage stored as a byte 38 | byte usage = ReentrantContextProvider.USAGE_TL_INACTIVE; 39 | /* 40 | * Reference to this instance (hard, soft or weak). 41 | * @see ReentrantContextProvider#refType 42 | */ 43 | Reference reference = null; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/sun/util/reentrant/ReentrantContextProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.util.reentrant; 27 | 28 | import java.lang.ref.Reference; 29 | import java.lang.ref.SoftReference; 30 | import java.lang.ref.WeakReference; 31 | 32 | /** 33 | * This abstract ReentrantContextProvider helper class manages the creation, 34 | * storage, and retrieval of concrete ReentrantContext instances which can be 35 | * subclassed to hold cached contextual data. 36 | * 37 | * It supports reentrancy as every call to acquire() provides a new unique context 38 | * instance that must later be returned for reuse by a call to release(ctx) 39 | * (typically in a try/finally block). 40 | * 41 | * It has a couple of abstract implementations which store references in a queue 42 | * and/or thread-local storage. 43 | * The Providers can be configured to hold ReentrantContext instances in memory 44 | * using hard, soft or weak references. 45 | * 46 | * The acquire() and release() methods are used to retrieve and return the contexts. 47 | * 48 | * The {@code newContext()} method remains abstract in all implementations and 49 | * must be provided by the module to create a new subclass of ReentrantContext 50 | * with the appropriate contextual data in it. 51 | * 52 | * Sample Usage: 53 | * - create a subclass ReentrantContextImpl to hold the thread state: 54 | * 55 | * static final class ReentrantContextImpl extends ReentrantContext { 56 | * // specific cached data 57 | * } 58 | * 59 | * - create the appropriate ReentrantContextProvider: 60 | * 61 | * private static final ReentrantContextProvider contextProvider = 62 | * new ReentrantContextProviderTL(ReentrantContextProvider.REF_WEAK) 63 | * { 64 | * @Override 65 | * protected ReentrantContextImpl newContext() { 66 | * return new ReentrantContextImpl(); 67 | * } 68 | * }; 69 | * ... 70 | * void someMethod() { 71 | * ReentrantContextImpl ctx = contextProvider.acquire(); 72 | * try { 73 | * // use the context 74 | * } finally { 75 | * contextProvider.release(ctx); 76 | * } 77 | * } 78 | * 79 | * @param ReentrantContext subclass 80 | * 81 | * @see ReentrantContext 82 | */ 83 | public abstract class ReentrantContextProvider 84 | { 85 | // thread-local storage: inactive 86 | static final byte USAGE_TL_INACTIVE = 0; 87 | // thread-local storage: in use 88 | static final byte USAGE_TL_IN_USE = 1; 89 | // CLQ storage 90 | static final byte USAGE_CLQ = 2; 91 | 92 | // hard reference 93 | public static final int REF_HARD = 0; 94 | // soft reference 95 | public static final int REF_SOFT = 1; 96 | // weak reference 97 | public static final int REF_WEAK = 2; 98 | 99 | /* members */ 100 | // internal reference type 101 | private final int refType; 102 | 103 | /** 104 | * Create a new ReentrantContext provider using the given reference type 105 | * among hard, soft or weak 106 | * 107 | * @param refType reference type 108 | */ 109 | protected ReentrantContextProvider(final int refType) { 110 | this.refType = refType; 111 | } 112 | 113 | /** 114 | * Create a new ReentrantContext instance 115 | * 116 | * @return new ReentrantContext instance 117 | */ 118 | protected abstract K newContext(); 119 | 120 | /** 121 | * Give a ReentrantContext instance for the current thread 122 | * 123 | * @return ReentrantContext instance 124 | */ 125 | public abstract K acquire(); 126 | 127 | /** 128 | * Restore the given ReentrantContext instance for reuse 129 | * 130 | * @param ctx ReentrantContext instance 131 | */ 132 | public abstract void release(K ctx); 133 | 134 | @SuppressWarnings("unchecked") 135 | protected final Reference getOrCreateReference(final K ctx) { 136 | if (ctx.reference == null) { 137 | // Create the reference: 138 | switch (refType) { 139 | case REF_HARD: 140 | ctx.reference = new HardReference(ctx); 141 | break; 142 | case REF_SOFT: 143 | ctx.reference = new SoftReference(ctx); 144 | break; 145 | default: 146 | case REF_WEAK: 147 | ctx.reference = new WeakReference(ctx); 148 | break; 149 | } 150 | } 151 | return (Reference) ctx.reference; 152 | } 153 | 154 | /* Missing HardReference implementation */ 155 | static final class HardReference extends WeakReference { 156 | // kept strong reference: 157 | private final V strongRef; 158 | 159 | HardReference(final V referent) { 160 | // no referent needed for the parent WeakReference: 161 | super(null); 162 | this.strongRef = referent; 163 | } 164 | 165 | @Override 166 | public V get() { 167 | return strongRef; 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/com/sun/util/reentrant/ReentrantContextProviderCLQ.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.util.reentrant; 27 | 28 | import java.lang.ref.Reference; 29 | import java.util.concurrent.ConcurrentLinkedQueue; 30 | 31 | /** 32 | * This ReentrantContextProvider implementation uses one ConcurrentLinkedQueue 33 | * to store all ReentrantContext instances (thread and its child contexts) 34 | * 35 | * Note: this implementation keeps less contexts in memory depending on the 36 | * concurrent active threads in contrary to a ThreadLocal provider. However, 37 | * it is slower in highly concurrent workloads. 38 | * 39 | * @param ReentrantContext subclass 40 | */ 41 | public abstract class ReentrantContextProviderCLQ 42 | extends ReentrantContextProvider 43 | { 44 | // ReentrantContext queue to store all contexts 45 | private final ConcurrentLinkedQueue> ctxQueue 46 | = new ConcurrentLinkedQueue>(); 47 | 48 | /** 49 | * Create a new ReentrantContext provider using the given reference type 50 | * among hard, soft or weak based using a ConcurrentLinkedQueue storage 51 | * 52 | * @param refType reference type 53 | */ 54 | public ReentrantContextProviderCLQ(final int refType) { 55 | super(refType); 56 | } 57 | 58 | /** 59 | * Give a ReentrantContext instance for the current thread 60 | * 61 | * @return ReentrantContext instance 62 | */ 63 | @Override 64 | public final K acquire() { 65 | K ctx = null; 66 | // Drain queue if all referent are null: 67 | Reference ref = null; 68 | while ((ctx == null) && ((ref = ctxQueue.poll()) != null)) { 69 | ctx = ref.get(); 70 | } 71 | if (ctx == null) { 72 | // create a new ReentrantContext if none is available 73 | ctx = newContext(); 74 | ctx.usage = USAGE_CLQ; 75 | } 76 | return ctx; 77 | } 78 | 79 | /** 80 | * Restore the given ReentrantContext instance for reuse 81 | * 82 | * @param ctx ReentrantContext instance 83 | */ 84 | @Override 85 | public final void release(final K ctx) { 86 | if (ctx.usage == USAGE_CLQ) { 87 | ctxQueue.offer(getOrCreateReference(ctx)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/sun/util/reentrant/ReentrantContextProviderTL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.sun.util.reentrant; 27 | 28 | import java.lang.ref.Reference; 29 | 30 | /** 31 | * This ReentrantContextProvider implementation uses a ThreadLocal to hold 32 | * the first ReentrantContext per thread and a ReentrantContextProviderCLQ to 33 | * store child ReentrantContext instances needed during recursion. 34 | * 35 | * Note: this implementation may keep up to one context in memory per thread. 36 | * Child contexts for recursive uses are stored in the queue using a WEAK 37 | * reference by default unless specified in the 2 argument constructor. 38 | * 39 | * @param ReentrantContext subclass 40 | */ 41 | public abstract class ReentrantContextProviderTL 42 | extends ReentrantContextProvider 43 | { 44 | // Thread-local storage: 45 | private final ThreadLocal> ctxTL 46 | = new ThreadLocal>(); 47 | 48 | // ReentrantContext CLQ provider for child contexts: 49 | private final ReentrantContextProviderCLQ ctxProviderCLQ; 50 | 51 | /** 52 | * Create a new ReentrantContext provider using the given reference type 53 | * among hard, soft or weak. 54 | * It uses weak reference for the child contexts. 55 | * 56 | * @param refType reference type 57 | */ 58 | public ReentrantContextProviderTL(final int refType) { 59 | this(refType, REF_WEAK); 60 | } 61 | 62 | /** 63 | * Create a new ReentrantContext provider using the given reference types 64 | * among hard, soft or weak 65 | * 66 | * @param refTypeTL reference type used by ThreadLocal 67 | * @param refTypeCLQ reference type used by ReentrantContextProviderCLQ 68 | */ 69 | public ReentrantContextProviderTL(final int refTypeTL, final int refTypeCLQ) 70 | { 71 | super(refTypeTL); 72 | 73 | final ReentrantContextProviderTL parent = this; 74 | 75 | this.ctxProviderCLQ = new ReentrantContextProviderCLQ(refTypeCLQ) { 76 | @Override 77 | protected K newContext() { 78 | return parent.newContext(); 79 | } 80 | }; 81 | } 82 | 83 | /** 84 | * Give a ReentrantContext instance for the current thread 85 | * 86 | * @return ReentrantContext instance 87 | */ 88 | @Override 89 | public final K acquire() { 90 | K ctx = null; 91 | final Reference ref = ctxTL.get(); 92 | if (ref != null) { 93 | ctx = ref.get(); 94 | } 95 | if (ctx == null) { 96 | // create a new ReentrantContext if none is available 97 | ctx = newContext(); 98 | // update thread local reference: 99 | ctxTL.set(getOrCreateReference(ctx)); 100 | } 101 | // Check reentrance: 102 | if (ctx.usage == USAGE_TL_INACTIVE) { 103 | ctx.usage = USAGE_TL_IN_USE; 104 | } else { 105 | // get or create another ReentrantContext from CLQ provider: 106 | ctx = ctxProviderCLQ.acquire(); 107 | } 108 | return ctx; 109 | } 110 | 111 | /** 112 | * Restore the given ReentrantContext instance for reuse 113 | * 114 | * @param ctx ReentrantContext instance 115 | */ 116 | @Override 117 | public final void release(final K ctx) { 118 | if (ctx.usage == USAGE_TL_IN_USE) { 119 | ctx.usage = USAGE_TL_INACTIVE; 120 | } else { 121 | ctxProviderCLQ.release(ctx); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/test/BigLeftSide.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.util.Arrays; 4 | 5 | import javafx.animation.AnimationTimer; 6 | import javafx.application.Application; 7 | import javafx.scene.Group; 8 | import javafx.scene.Scene; 9 | import javafx.scene.canvas.Canvas; 10 | import javafx.scene.canvas.GraphicsContext; 11 | import javafx.scene.paint.Color; 12 | import javafx.stage.Stage; 13 | 14 | public class BigLeftSide extends Application { 15 | public static final int NUM_SHAPES = 100; 16 | public static final int PATH_BORDER = 20; 17 | public static final int PATH_SIZE = 400; 18 | public static final int SHIFT_SIZE = 20; 19 | public static final long[] FRAME_TIMES = new long[60]; 20 | public static final double NANO_SCALE = 1000.0 * 1000.0 * 1000.0; 21 | public static final double FRAME_AVG_SCALE = FRAME_TIMES.length * NANO_SCALE; 22 | 23 | @Override 24 | public void start(Stage stage) { 25 | int CV_DIM = PATH_BORDER + PATH_SIZE + SHIFT_SIZE + PATH_BORDER; 26 | Canvas cv = new Canvas(CV_DIM, CV_DIM); 27 | GraphicsContext gc = cv.getGraphicsContext2D(); 28 | renderFrame(gc); 29 | 30 | Scene scene = new Scene(new Group(cv)); 31 | stage.setScene(scene); 32 | stage.show(); 33 | 34 | AnimationTimer timer = new AnimationTimer() { 35 | long nsStart; 36 | long nsAverageTotal; 37 | int delay = 20; 38 | 39 | @Override 40 | public void handle(long nsNow) { 41 | if (nsStart > 0) { 42 | long nsFrame = (nsNow - nsStart); 43 | if (delay > 0) { 44 | delay--; 45 | System.out.printf("Warming up with %f fps\n", NANO_SCALE / nsFrame); 46 | } else { 47 | if (nsAverageTotal == 0) { 48 | Arrays.fill(FRAME_TIMES, nsFrame); 49 | nsAverageTotal = nsFrame * FRAME_TIMES.length; 50 | } else { 51 | nsAverageTotal -= FRAME_TIMES[0]; 52 | System.arraycopy(FRAME_TIMES, 1, FRAME_TIMES, 0, FRAME_TIMES.length - 1); 53 | FRAME_TIMES[FRAME_TIMES.length-1] = nsFrame; 54 | nsAverageTotal += nsFrame; 55 | } 56 | System.out.printf("average frame rate = %f fps\n", 57 | (FRAME_AVG_SCALE / nsAverageTotal)); 58 | } 59 | } 60 | nsStart = nsNow; 61 | renderFrame(gc); 62 | } 63 | }; 64 | timer.start(); 65 | } 66 | 67 | void renderFrame(GraphicsContext gc) { 68 | int l1 = NUM_SHAPES / 4; 69 | int l2 = NUM_SHAPES / 2; 70 | int l3 = NUM_SHAPES * 3 / 4; 71 | int l4 = NUM_SHAPES; 72 | double s1 = l1; 73 | double s2 = l2 - l1; 74 | double s3 = l3 - l2; 75 | double s4 = l4 - l3; 76 | for (int i = 0; i < NUM_SHAPES; i++) { 77 | gc.save(); 78 | gc.setFill(new Color(Math.random(), Math.random(), Math.random(), 1.0)); 79 | double x, y; 80 | if (i < l1) { 81 | x = i / s1; 82 | y = 0.0; 83 | } else if (i < l2) { 84 | x = 1.0; 85 | y = (i - l1) / s2; 86 | } else if (i < l3) { 87 | x = (l3 - i) / s3; 88 | y = 1.0; 89 | } else { 90 | x = 0.0; 91 | y = (l4 - i) / s4; 92 | } 93 | gc.translate(PATH_BORDER + x * SHIFT_SIZE, 94 | PATH_BORDER + y * SHIFT_SIZE); 95 | gc.beginPath(); 96 | gc.moveTo(0.0, 0.0); 97 | gc.lineTo(1.0, 0.0); 98 | gc.lineTo(1.0, 1.0); 99 | gc.lineTo(0.0, 1.0); 100 | gc.closePath(); 101 | gc.moveTo(PATH_SIZE, 0.0); 102 | gc.lineTo(PATH_SIZE, PATH_SIZE); 103 | gc.lineTo(PATH_SIZE - PATH_BORDER, PATH_SIZE); 104 | gc.lineTo(PATH_SIZE - PATH_BORDER, 0.0); 105 | gc.closePath(); 106 | gc.fill(); 107 | gc.restore(); 108 | } 109 | } 110 | 111 | /** 112 | * Java main for when running without JavaFX launcher 113 | */ 114 | public static void main(String[] args) { 115 | 116 | System.setProperty("prism.verbose", "true"); 117 | 118 | launch(args); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/test/PathApp.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.util.List; 4 | import javafx.application.Application; 5 | import javafx.scene.Parent; 6 | import javafx.scene.Scene; 7 | import javafx.scene.layout.Pane; 8 | import javafx.scene.layout.Region; 9 | import javafx.scene.paint.Color; 10 | import javafx.scene.shape.ArcTo; 11 | import javafx.scene.shape.ClosePath; 12 | import javafx.scene.shape.CubicCurveTo; 13 | import javafx.scene.shape.HLineTo; 14 | import javafx.scene.shape.LineTo; 15 | import javafx.scene.shape.MoveTo; 16 | import javafx.scene.shape.Path; 17 | import javafx.scene.shape.PathElement; 18 | import javafx.scene.shape.QuadCurveTo; 19 | import javafx.scene.shape.VLineTo; 20 | import javafx.stage.Stage; 21 | 22 | /** 23 | * A sample that demonstrates two path shapes. 24 | */ 25 | public class PathApp extends Application { 26 | 27 | public Parent createContent() { 28 | Pane root = new Pane(); 29 | root.setPrefSize(505, 300); 30 | root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); 31 | root.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); 32 | // Create path shape - square 33 | final Path path1 = new Path(); 34 | path1.getElements().addAll( 35 | new MoveTo(25, 25), 36 | new HLineTo(65), 37 | new VLineTo(65), 38 | new LineTo(25, 65), 39 | new ClosePath()); 40 | path1.setFill(null); 41 | path1.setStroke(Color.RED); 42 | path1.setStrokeWidth(2); 43 | 44 | // Create path shape - curves 45 | final Path path2 = new Path(); 46 | path2.getElements().addAll( 47 | new MoveTo(100, 45), 48 | new CubicCurveTo(120, 20, 130, 80, 140, 45), 49 | new QuadCurveTo(150, 0, 160, 45), 50 | new ArcTo(20, 40, 0, 180, 45, true, true)); 51 | path2.setFill(null); 52 | path2.setStroke(Color.DODGERBLUE); 53 | path2.setStrokeWidth(2); 54 | path2.setTranslateY(36); 55 | 56 | // Create path shape - curves 57 | final Path path3 = new Path(); 58 | path3.getElements().addAll( 59 | new MoveTo(100, 45), 60 | new CubicCurveTo(120, 20, 130, 80, 140, 45), 61 | new QuadCurveTo(150, 0, 160, 45), 62 | new ArcTo(20, 40, 0, 180, 45, true, true)); 63 | path3.setFill(Color.ORANGE); 64 | path3.setStroke(null); 65 | path3.setTranslateY(136); 66 | 67 | final Path path4 = new Path(); 68 | path4.setFill(null); 69 | path4.setStroke(Color.GREEN); 70 | path4.setStrokeWidth(2); 71 | 72 | final List pe = path4.getElements(); 73 | pe.add(new MoveTo(100, 100)); 74 | 75 | for (int i = 0; i < 20000; i++) { 76 | pe.add(new LineTo(110 + 0.01f * i, 110)); 77 | pe.add(new LineTo(111 + 0.01f * i, 100)); 78 | } 79 | 80 | pe.add(new LineTo(Float.NaN, 200)); 81 | pe.add(new LineTo(200, 200)); 82 | pe.add(new LineTo(200, Float.NaN)); 83 | pe.add(new LineTo(300, 300)); 84 | pe.add(new LineTo(Float.NaN, Float.NaN)); 85 | pe.add(new LineTo(100, 200)); 86 | pe.add(new ClosePath()); 87 | 88 | // Test basic horizontal line (on pixel centers) 89 | final Path path5 = new Path(); 90 | path5.getElements().addAll( 91 | new MoveTo(9.5, 9.5), 92 | new HLineTo(100)); 93 | path5.setStroke(Color.PINK); 94 | path5.setStrokeWidth(1.0); 95 | 96 | // Fill contiguous shapes on NonAA rasterizer: 97 | double[] x = new double[] { 33.3333, 100.56677}; 98 | 99 | final Path path6 = new Path(); 100 | path6.getElements().addAll( 101 | new MoveTo(9.5, 9.5), 102 | new HLineTo(100), 103 | new LineTo(x[0], x[1]), 104 | new HLineTo(9.5), 105 | new ClosePath()); 106 | path6.setFill(Color.RED); 107 | path6.setStroke(null); 108 | path6.setSmooth(false); 109 | 110 | final Path path7 = new Path(); 111 | path7.getElements().addAll( 112 | new MoveTo(200, 9.5), 113 | new HLineTo(100), 114 | new LineTo(x[0], x[1]), 115 | new HLineTo(200), 116 | new ClosePath()); 117 | path7.setFill(Color.BLUE); 118 | path7.setStroke(null); 119 | path7.setSmooth(false); 120 | 121 | root.getChildren().addAll(path6, path7, path1, path2, path3, path4, path5); 122 | return root; 123 | } 124 | 125 | @Override 126 | public void start(Stage primaryStage) throws Exception { 127 | primaryStage.setScene(new Scene(createContent())); 128 | primaryStage.show(); 129 | } 130 | 131 | /** 132 | * Java main for when running without JavaFX launcher 133 | */ 134 | public static void main(String[] args) { 135 | 136 | System.setProperty("prism.verbose", "true"); 137 | 138 | launch(args); 139 | } 140 | } -------------------------------------------------------------------------------- /src/main/java/test/PathBug.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | */ 23 | package test; 24 | 25 | import javafx.application.Application; 26 | import javafx.scene.Scene; 27 | import javafx.scene.canvas.Canvas; 28 | import javafx.scene.canvas.GraphicsContext; 29 | import javafx.scene.layout.StackPane; 30 | import javafx.scene.paint.Color; 31 | import javafx.stage.Stage; 32 | 33 | public class PathBug extends Application { 34 | 35 | @Override 36 | public void start(Stage primaryStage) { 37 | double w = 400; 38 | double h = 400; 39 | 40 | Canvas canvas = new Canvas(w, h); 41 | GraphicsContext gc = canvas.getGraphicsContext2D(); 42 | 43 | gc.setFill(Color.BLACK); 44 | gc.fillRect(0, 0, w, h); 45 | 46 | if (true) { 47 | // Setting the clip to exclude the lower 50 pixels. 48 | gc.beginPath(); 49 | gc.rect(0, 0, w, h - 50); 50 | gc.closePath(); 51 | gc.clip(); // Comment this line out to see the intended shape. 52 | } 53 | 54 | // Constructing a path that should look like a square with a hole. 55 | gc.beginPath(); 56 | gc.rect(20, 20, w - 40, h - 40); 57 | gc.arc(200, 200, 100, 100, 0, 360); 58 | gc.closePath(); 59 | 60 | gc.setFill(Color.CORNFLOWERBLUE); 61 | gc.fill(); 62 | 63 | StackPane root = new StackPane(); 64 | root.getChildren().add(canvas); 65 | Scene scene = new Scene(root); 66 | 67 | primaryStage.setScene(scene); 68 | primaryStage.show(); 69 | } 70 | 71 | public static void main(String[] args) { 72 | Application.launch(args); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/test/PolygonClipTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | */ 23 | package test; 24 | 25 | import javafx.animation.AnimationTimer; 26 | import javafx.application.Application; 27 | import javafx.scene.Group; 28 | import javafx.scene.Scene; 29 | import javafx.scene.paint.Color; 30 | import javafx.scene.shape.ClosePath; 31 | import javafx.scene.shape.CubicCurveTo; 32 | import javafx.scene.shape.LineTo; 33 | import javafx.scene.shape.MoveTo; 34 | import javafx.scene.shape.Path; 35 | import javafx.scene.transform.Scale; 36 | import javafx.stage.Stage; 37 | 38 | /** 39 | * Test dedicated to study special cases in Path clipper 40 | */ 41 | public class PolygonClipTest extends Application { 42 | 43 | private static final boolean TEST_STROKE = false; 44 | private static final int TEST_CASE = 13; 45 | 46 | private static final int SPEED = 60; 47 | 48 | private static final double SCENE_WIDTH = 100.0; 49 | private static final double SCALE = 3.0; 50 | 51 | static { 52 | System.setProperty("prism.marlin.subPixel_log2_X", "8"); 53 | 54 | // disable static clipping setting: 55 | System.setProperty("prism.marlin.clip", "false"); 56 | System.setProperty("prism.marlin.clip.runtime.enable", "true"); 57 | 58 | // disable min length check: always subdivide curves at clip edges 59 | //System.setProperty("prism.marlin.clip.subdivider.minLength", "-1"); 60 | if (false) { 61 | System.setProperty("prism.marlin.clip.subdivider.minLength", "100"); 62 | } else { 63 | System.setProperty("prism.marlin.clip.subdivider.minLength", "-1"); 64 | } 65 | } 66 | 67 | @Override 68 | public void start(Stage stage) { 69 | final Path p = new Path(); 70 | p.setFill(Color.STEELBLUE); 71 | if (TEST_STROKE) { 72 | p.setStroke(Color.RED); 73 | p.setStrokeWidth(1.0 / SCALE); 74 | } else { 75 | p.setStroke(null); 76 | } 77 | p.setCache(false); 78 | p.setSmooth(true); 79 | 80 | final Group group = new Group(p); 81 | if (SCALE != 1.0) { 82 | group.getTransforms().add(new Scale(SCALE, SCALE)); 83 | } 84 | 85 | final Scene scene = new Scene(group, SCALE * SCENE_WIDTH, SCALE * SCENE_WIDTH, Color.LIGHTGRAY); 86 | stage.setScene(scene); 87 | stage.setTitle(this.getClass().getSimpleName()); 88 | stage.show(); 89 | 90 | final AnimationTimer anim = new AnimationTimer() { 91 | int numHandle = 0; 92 | boolean clip = false; 93 | 94 | @Override 95 | public void handle(long now) { 96 | if ((numHandle++) % SPEED == 0) { 97 | clip = !clip; 98 | System.out.println("clip: " + clip); 99 | 100 | // Enable or Disable clipping: 101 | System.setProperty("prism.marlin.clip.runtime", (clip) ? "true" : "false"); 102 | 103 | p.setFill((clip) ? Color.BLUE : Color.GREEN); 104 | updatePath(p); 105 | } 106 | } 107 | }; 108 | anim.start(); 109 | } 110 | 111 | private static void updatePath(final Path p) { 112 | // modifying path for every test ensures no caching (bounds...) 113 | switch (TEST_CASE) { 114 | case 1: 115 | p.getElements().setAll( 116 | new MoveTo(-89.687675, 171.33438), 117 | new LineTo(112.86113, 151.63348), 118 | new LineTo(84.55856, 111.90166), 119 | new LineTo(-2.169856, -22.49296), 120 | new LineTo(155.98148, -65.42218), 121 | new LineTo(100.07923, 96.86355), 122 | new ClosePath() 123 | ); 124 | break; 125 | case 2: 126 | p.getElements().setAll( 127 | new MoveTo(-57.112507, 79.05166), 128 | new LineTo(136.05696, 26.617828), 129 | new LineTo(92.853615, 62.25588), 130 | new LineTo(59.66546, 56.429977), 131 | new LineTo(79.59174, -54.074516), 132 | new LineTo(-75.93232, 39.502163), 133 | new ClosePath(), 134 | new LineTo(86.28725, -18.620544), 135 | new LineTo(87.534134, -69.6632), 136 | new LineTo(-61.609844, -46.943455), 137 | new LineTo(-53.029644, -40.836628), 138 | new LineTo(89.01727, -43.499767), 139 | new ClosePath() 140 | ); 141 | break; 142 | case 3: 143 | p.getElements().setAll( 144 | new MoveTo(174.8631, 124.340775), 145 | new LineTo(-13.485423, 120.01353), 146 | new LineTo(-40.214275, -11.351073), 147 | new LineTo(96.66595, 33.508484), 148 | new LineTo(-31.891193, -17.238123), 149 | new LineTo(-0.092007555, 49.85812), 150 | new ClosePath(), 151 | new LineTo(-35.58541, 126.748764), 152 | new LineTo(13.534866, 105.12724), 153 | new LineTo(-84.706535, 165.25713), 154 | new LineTo(105.69439, 48.8846), 155 | new LineTo(-34.655937, 88.94304), 156 | new ClosePath() 157 | ); 158 | break; 159 | case 4: 160 | p.getElements().setAll( 161 | new MoveTo(-99.77336, 35.190475), 162 | new CubicCurveTo(-25.539629, 180.36601, 52.512184, 42.104904, -66.391945, -7.1875143), 163 | new CubicCurveTo(97.41586, 79.37796, 102.07544, 10.436856, -7.376722, 18.136734) 164 | ); 165 | break; 166 | case 5: 167 | // move / close outside: 168 | p.getElements().setAll( 169 | new MoveTo(-99.77336, 35.190475), 170 | new ClosePath() 171 | ); 172 | break; 173 | case 6: 174 | // move / close inside: 175 | p.getElements().setAll( 176 | new MoveTo(13.77336, 35.190475), 177 | new ClosePath() 178 | ); 179 | break; 180 | case 7: 181 | // move / close outside: 182 | p.getElements().setAll( 183 | new MoveTo(-99.77336, 35.190475) 184 | ); 185 | break; 186 | case 8: 187 | // move / close outside: 188 | p.getElements().setAll( 189 | new MoveTo(-99.77336, 35.190475), 190 | new LineTo(-133,-100) 191 | ); 192 | break; 193 | case 9: 194 | // move / close outside: 195 | p.getElements().setAll( 196 | new MoveTo(-99.77336, 35.190475), 197 | new LineTo(-133,-100), 198 | new ClosePath() 199 | ); 200 | break; 201 | case 10: 202 | // move / close inside: 203 | p.getElements().setAll( 204 | new MoveTo(13.77336, 35.190475), 205 | new LineTo(73, 60), 206 | new ClosePath() 207 | ); 208 | break; 209 | case 13: 210 | // move / line (outside over corners) then close => inside: 211 | p.getElements().setAll( 212 | new MoveTo(-99.77336, 35.190475), 213 | new LineTo(-23, -37), 214 | new LineTo(173, -37), 215 | new LineTo(173, 57), 216 | new ClosePath() 217 | ); 218 | break; 219 | default: 220 | } 221 | } 222 | 223 | public static void main(String[] args) { 224 | launch(args); 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/test/PolygonTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | */ 23 | package test; 24 | 25 | import javafx.application.Application; 26 | import javafx.scene.Group; 27 | import javafx.scene.Scene; 28 | import javafx.scene.paint.Color; 29 | import javafx.scene.shape.Polygon; 30 | import javafx.scene.transform.Translate; 31 | import javafx.stage.Screen; 32 | import javafx.stage.Stage; 33 | 34 | /** 35 | */ 36 | public class PolygonTest extends Application { 37 | 38 | // (2^31 = 1073741824) / 256 = 4194304 => overflow in DRenderer 39 | private static final double LARGE_X_COORDINATE = 4194304.250; // -Dprism.marlin.subPixel_log2_X=8 40 | // private static final Double LARGE_X_COORDINATE = 134217728.250; // -Dprism.marlin.subPixel_log2_X=3 41 | 42 | // private static final double epsilon = 1.5E10; // max power of ten before translation into viewport is wrong 43 | private static final double epsilon = 1000; 44 | 45 | private static final double SCENE_WIDTH = 600.0; 46 | 47 | @Override 48 | public void start(Stage stage) { 49 | double dpi = Screen.getPrimary().getDpi(); 50 | System.out.println("dpi: " + dpi); 51 | 52 | double dpiScale = 1.0; // Screen.getPrimary().getOutputScaleX(); 53 | 54 | double longWidth = LARGE_X_COORDINATE / dpiScale + SCENE_WIDTH + 0.001 + epsilon; 55 | 56 | final Polygon veryWidePolygon; 57 | if (true) { 58 | if (true) { 59 | veryWidePolygon = new Polygon( 60 | 0.0, -1000.0, 61 | 100.0, -500.0, 62 | longWidth, 200.0, 63 | longWidth - 1000, 500.0 64 | ); 65 | 66 | } else { 67 | // inverted test case => no large moveTo in Filler but bug in Stroker: 68 | if (true) { 69 | veryWidePolygon = new Polygon( 70 | 0.0, 100.0, 71 | 0.0, 0.0, 72 | longWidth, 50.0, 73 | longWidth, 100.0 74 | ); 75 | } else { 76 | veryWidePolygon = new Polygon( 77 | longWidth, 50.0, 78 | longWidth, 100.0, 79 | 0.0, 100.0, 80 | 0.0, 0.0 81 | ); 82 | } 83 | } 84 | } else { 85 | // original test case => large moveTo in Filler but no bug in Stroker: 86 | veryWidePolygon = new Polygon( 87 | 0.0, 0.0, 88 | longWidth, 50.0, 89 | longWidth, 100.0, 90 | 0.0, 100.0); 91 | } 92 | 93 | veryWidePolygon.setFill(Color.STEELBLUE); 94 | veryWidePolygon.setStroke(Color.RED); 95 | veryWidePolygon.setStrokeWidth(5); 96 | 97 | Group group = new Group(veryWidePolygon); 98 | group.getTransforms().add(new Translate(-longWidth + SCENE_WIDTH, 100.0)); 99 | 100 | Scene scene = new Scene(group, SCENE_WIDTH, 400, Color.LIGHTGRAY); 101 | stage.setScene(scene); 102 | stage.setTitle("DPI scale: " + dpiScale); 103 | stage.show(); 104 | } 105 | 106 | public static void main(String[] args) { 107 | launch(args); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/test/PolylineClipTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | */ 23 | package test; 24 | 25 | import javafx.animation.AnimationTimer; 26 | import javafx.application.Application; 27 | import javafx.scene.Group; 28 | import javafx.scene.Scene; 29 | import javafx.scene.paint.Color; 30 | import javafx.scene.shape.LineTo; 31 | import javafx.scene.shape.MoveTo; 32 | import javafx.scene.shape.Path; 33 | import javafx.stage.Stage; 34 | 35 | public class PolylineClipTest extends Application { 36 | 37 | static final int NUM_OFFSCREEN = 10000; 38 | static boolean OMIT_OFFSCREEN = false; 39 | 40 | @Override 41 | public void start(Stage stage) { 42 | Path p = new Path(); 43 | p.setStroke(Color.BLUE); 44 | p.setFill(null); 45 | 46 | if (OMIT_OFFSCREEN) { 47 | p.getElements().add(new MoveTo(-100, 100)); 48 | } else { 49 | p.getElements().add(new MoveTo(-500, 100)); 50 | for (int i = 0; i < NUM_OFFSCREEN; i++) { 51 | double x = Math.random() * 400 - 500; 52 | double y = Math.random() * 200; 53 | p.getElements().add(new LineTo(x, y)); 54 | } 55 | p.getElements().add(new LineTo(-100, 100)); 56 | } 57 | 58 | final LineTo lt1 = new LineTo(50, 150); 59 | final LineTo lt2 = new LineTo(150, 50); 60 | p.getElements().add(lt1); 61 | p.getElements().add(lt2); 62 | p.setCache(false); 63 | 64 | Scene scene = new Scene(new Group(p), 200, 200); 65 | stage.setScene(scene); 66 | stage.show(); 67 | 68 | AnimationTimer anim = new AnimationTimer() { 69 | @Override 70 | public void handle(long now) { 71 | double x1 = Math.random() * 20 + 40; 72 | double y1 = Math.random() * 20 + 140; 73 | double x2 = Math.random() * 20 + 140; 74 | double y2 = Math.random() * 20 + 40; 75 | lt1.setX(x1); 76 | lt1.setY(y1); 77 | lt2.setX(x2); 78 | lt2.setY(y2); 79 | } 80 | }; 81 | anim.start(); 82 | } 83 | 84 | public static void main(String argv[]) { 85 | OMIT_OFFSCREEN = (argv.length != 0); 86 | System.out.println("OMIT_OFFSCREEN: " + OMIT_OFFSCREEN); 87 | launch(argv); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/test/RasterPerf.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.sun.javafx.geom.Ellipse2D; 4 | import com.sun.javafx.geom.Path2D; 5 | import com.sun.javafx.geom.RectBounds; 6 | import com.sun.javafx.geom.RoundRectangle2D; 7 | import com.sun.javafx.geom.Shape; 8 | import com.sun.javafx.geom.transform.BaseTransform; 9 | import com.sun.prism.impl.shape.DMarlinRasterizer; 10 | import com.sun.prism.impl.shape.MarlinRasterizer; 11 | import com.sun.prism.impl.shape.NativePiscesRasterizer; 12 | import com.sun.prism.impl.shape.OpenPiscesRasterizer; 13 | import com.sun.prism.impl.shape.ShapeRasterizer; 14 | 15 | public class RasterPerf { 16 | 17 | static final int PRIME_CALLS = 1000; 18 | static final long warmupns = 1000l * 1000l * 500l; 19 | static final long targetns = 1000l * 1000l * 3000l; 20 | 21 | static String allresults = ""; 22 | static final Path2D cubics, quads; 23 | 24 | static { 25 | cubics = new Path2D(); 26 | cubics.moveTo(10, 10); 27 | cubics.curveTo(110, 10, 10, 15, 110, 20); 28 | cubics.curveTo(10, 20, 110, 25, 10, 30); 29 | cubics.curveTo(110, 30, 10, 35, 110, 40); 30 | cubics.curveTo(10, 40, 110, 45, 10, 50); 31 | cubics.curveTo(110, 50, 10, 55, 110, 60); 32 | cubics.curveTo(10, 60, 110, 65, 10, 70); 33 | cubics.curveTo(110, 70, 10, 75, 110, 80); 34 | cubics.curveTo(10, 80, 110, 85, 10, 90); 35 | cubics.curveTo(110, 90, 10, 95, 110, 100); 36 | cubics.curveTo(10, 100, 110, 105, 10, 110); 37 | quads = new Path2D(); 38 | quads.moveTo(60, 10); 39 | quads.quadTo(110, 15, 60, 20); 40 | quads.quadTo(10, 25, 60, 30); 41 | quads.quadTo(110, 35, 60, 40); 42 | quads.quadTo(10, 45, 60, 50); 43 | quads.quadTo(110, 55, 60, 60); 44 | quads.quadTo(10, 65, 60, 70); 45 | quads.quadTo(110, 75, 60, 80); 46 | quads.quadTo(10, 85, 60, 90); 47 | quads.quadTo(110, 95, 60, 100); 48 | quads.quadTo(10, 105, 60, 110); 49 | } 50 | 51 | public static void bench(ShapeRasterizer sr, Shape s, boolean aa, String name) { 52 | RectBounds b = s.getBounds(); 53 | for (int i = 0; i < PRIME_CALLS; i++) { 54 | sr.getMaskData(s, null, b, BaseTransform.IDENTITY_TRANSFORM, true, aa); 55 | } 56 | long start, elapsed; 57 | int n = 0; 58 | start = System.nanoTime(); 59 | do { 60 | sr.getMaskData(s, null, b, BaseTransform.IDENTITY_TRANSFORM, true, aa); 61 | n++; 62 | elapsed = System.nanoTime() - start; 63 | } while (elapsed < warmupns); 64 | long limit = targetns * n / elapsed; 65 | System.out.println("warmup: " + n + " iterations in " + elapsed + "ns"); 66 | System.out.println("benchmarking " + limit + " iterations"); 67 | System.out.flush(); 68 | try { 69 | Thread.sleep(100); 70 | } catch (InterruptedException e) { 71 | } 72 | start = System.nanoTime(); 73 | for (int i = 0; i < limit; i++) { 74 | sr.getMaskData(s, null, b, BaseTransform.IDENTITY_TRANSFORM, true, aa); 75 | } 76 | long end = System.nanoTime(); 77 | double ms = (end - start) / 1000.0 / 1000.0; 78 | ms = Math.round(ms * 100.0) / 100.0; 79 | String result = limit + " " + name + " rasterizations took " 80 | + ms + "ms, " + (limit * 1000) / ms + " ops/sec\n"; 81 | System.out.println(result); 82 | allresults += result; 83 | } 84 | 85 | public static void bench(ShapeRasterizer sr, String srname, boolean aa) { 86 | bench(sr, new RoundRectangle2D(10, 10, 10, 10, 0, 0), aa, "10x10 Rectangle " + srname); 87 | bench(sr, new RoundRectangle2D(10, 10, 100, 100, 0, 0), aa, "100x100 Rectangle " + srname); 88 | bench(sr, new RoundRectangle2D(10, 10, 300, 300, 0, 0), aa, "300x300 Rectangle " + srname); 89 | bench(sr, new Ellipse2D(10, 10, 10, 10), aa, "10x10 Ellipse " + srname); 90 | bench(sr, new Ellipse2D(10, 10, 100, 100), aa, "100x100 Ellipse " + srname); 91 | bench(sr, new Ellipse2D(10, 10, 300, 300), aa, "300x300 Ellipse " + srname); 92 | bench(sr, cubics, aa, "100x100 Cubics " + srname); 93 | bench(sr, quads, aa, "100x100 Quads " + srname); 94 | } 95 | 96 | public static void bench(ShapeRasterizer sr, String srname) { 97 | bench(sr, srname + " non-AA", false); 98 | allresults += "\n"; 99 | bench(sr, srname + " AA", true); 100 | allresults += "\n\n"; 101 | } 102 | 103 | public static void main(String argv[]) { 104 | for (int n = 0; n < 2; n++) { 105 | // bench(new NativePiscesRasterizer(), "native"); 106 | // bench(new OpenPiscesRasterizer(), "Java"); 107 | bench(new MarlinRasterizer(), "MarlinFX"); 108 | bench(new DMarlinRasterizer(), "DMarlinFX"); 109 | System.out.println(); 110 | System.out.println(allresults); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/test/ShapeOutlineBug.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javafx.application.Application; 4 | import javafx.scene.Scene; 5 | import javafx.scene.layout.Pane; 6 | import javafx.scene.paint.Color; 7 | import javafx.scene.shape.Circle; 8 | import javafx.stage.Stage; 9 | 10 | public class ShapeOutlineBug extends Application { 11 | 12 | private final static double SIZE = 900; 13 | private final static double D = 0.5 * SIZE; 14 | private final static double sqrt2 = Math.sqrt(2); 15 | 16 | public static void main(String[] args) { 17 | Application.launch(args); 18 | } 19 | 20 | @Override 21 | public void start(Stage stage) throws Exception { 22 | double r = 1843200.0; 23 | double c = D - r / sqrt2; 24 | 25 | Circle shape = new Circle(c, c, r, Color.GREY); 26 | 27 | shape.setStroke(Color.BLACK); 28 | shape.setStrokeWidth(2.0); 29 | shape.getStrokeDashArray().addAll(10.0, 5.0); 30 | 31 | Pane root = new Pane(); 32 | root.getChildren().add(shape); 33 | 34 | stage.setScene(new Scene(root, SIZE, SIZE)); 35 | stage.show(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/test/ShapeOutlineBugCirclePath.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javafx.application.Application; 4 | import javafx.scene.Scene; 5 | import javafx.scene.layout.Pane; 6 | import javafx.scene.paint.Color; 7 | import javafx.scene.shape.Circle; 8 | import javafx.scene.shape.Shape; 9 | import javafx.scene.shape.PathElement; 10 | import javafx.scene.shape.Path; 11 | import javafx.scene.shape.MoveTo; 12 | import javafx.scene.shape.LineTo; 13 | import javafx.scene.shape.QuadCurveTo; 14 | import javafx.scene.shape.CubicCurveTo; 15 | import javafx.scene.shape.ClosePath; 16 | import javafx.stage.Stage; 17 | 18 | public class ShapeOutlineBugCirclePath extends Application { 19 | 20 | private final static double SIZE = 900; 21 | private final static double D = 0.5 * SIZE; 22 | private final static double sqrt2 = Math.sqrt(2); 23 | private static boolean SMALL = false; 24 | 25 | public static void main(String[] args) { 26 | SMALL = args.length > 0; 27 | Application.launch(args); 28 | } 29 | 30 | public static void printShape(Shape s) { 31 | if (!(s instanceof Path)) { 32 | System.out.println("Not a path: " + s); 33 | return; 34 | } 35 | System.out.println("Path["); 36 | for (PathElement pe : ((Path) s).getElements()) { 37 | if (pe instanceof MoveTo) { 38 | MoveTo mt = (MoveTo) pe; 39 | System.out.println(" MoveTo (" + mt.getX() + ", " + mt.getY() + ")"); 40 | } else if (pe instanceof LineTo) { 41 | LineTo lt = (LineTo) pe; 42 | System.out.println(" LineTo (" + lt.getX() + ", " + lt.getY() + ")"); 43 | } else if (pe instanceof QuadCurveTo) { 44 | QuadCurveTo lt = (QuadCurveTo) pe; 45 | System.out.println(" QuadCurveTo (" + lt.getControlX() + ", " + lt.getControlY() + ", "); 46 | System.out.println(" " + lt.getX() + ", " + lt.getY() + ")"); 47 | } else if (pe instanceof CubicCurveTo) { 48 | CubicCurveTo lt = (CubicCurveTo) pe; 49 | System.out.println(" CubicCurveTo(" + lt.getControlX1() + ", " + lt.getControlY1() + ", "); 50 | System.out.println(" " + lt.getControlX2() + ", " + lt.getControlY2() + ", "); 51 | System.out.println(" " + lt.getX() + ", " + lt.getY() + ")"); 52 | } else if (pe instanceof ClosePath) { 53 | System.out.println(" ClosePath ()"); 54 | } else { 55 | System.out.println("Unrecognized path element: " + pe); 56 | } 57 | } 58 | System.out.println("]"); 59 | } 60 | 61 | @Override 62 | public void start(Stage stage) throws Exception { 63 | double r = SMALL ? 100 : 1843200.0; 64 | double c = D - r / sqrt2; 65 | 66 | Circle circle = new Circle(c, c, r, Color.GREY); 67 | Circle littlecircle = new Circle(c, c, 10, Color.GREY); 68 | Shape shape = Shape.union(circle, littlecircle); 69 | printShape(shape); 70 | 71 | shape.setFill(Color.BLUE); 72 | shape.setStroke(Color.RED); 73 | shape.setStrokeWidth(2.0); 74 | shape.getStrokeDashArray().addAll(10.0, 5.0); 75 | 76 | Pane root = new Pane(); 77 | root.getChildren().add(shape); 78 | 79 | stage.setScene(new Scene(root, SIZE, SIZE)); 80 | stage.show(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/test/ShapeOutlineBugRectangle.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javafx.application.Application; 4 | import javafx.scene.Scene; 5 | import javafx.scene.layout.Pane; 6 | import javafx.scene.paint.Color; 7 | import javafx.scene.shape.ClosePath; 8 | import javafx.scene.shape.LineTo; 9 | import javafx.scene.shape.MoveTo; 10 | import javafx.scene.shape.Path; 11 | import javafx.stage.Stage; 12 | 13 | public class ShapeOutlineBugRectangle extends Application { 14 | private final static double SIZE = 900 * 1024 * 5; 15 | 16 | public static void main(String[] args) { 17 | Application.launch(args); 18 | } 19 | 20 | @Override 21 | public void start(Stage stage) throws Exception { 22 | Path shape = new Path(new MoveTo(450, 450), 23 | new LineTo(-SIZE, -SIZE), 24 | new LineTo(0, -2 * SIZE), 25 | new LineTo(SIZE, -SIZE), 26 | new LineTo(450, 450), 27 | new ClosePath()); 28 | 29 | shape.setFill(Color.BLUE); 30 | shape.setStroke(Color.RED); 31 | shape.setStrokeWidth(2.0); 32 | shape.getStrokeDashArray().addAll(10.0, 5.0); 33 | 34 | Pane root = new Pane(); 35 | root.getChildren().add(shape); 36 | 37 | stage.setScene(new Scene(root, 900, 900)); 38 | stage.show(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/test/ShapePerformanceBug.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javafx.application.Application; 4 | import javafx.scene.Scene; 5 | import javafx.scene.layout.Pane; 6 | import javafx.scene.paint.Color; 7 | import javafx.scene.shape.Circle; 8 | import javafx.stage.Stage; 9 | 10 | public class ShapePerformanceBug extends Application { 11 | private final static double SIZE = 900; 12 | private final static double D = 0.5 * SIZE; 13 | private final static double sqrt2 = Math.sqrt(2); 14 | 15 | public static void main(String[] args) { 16 | Application.launch(args); 17 | } 18 | 19 | @Override 20 | public void start(Stage stage) throws Exception { 21 | double r = 1843200.0; 22 | double c = D - r/sqrt2; 23 | 24 | Circle shape = new Circle(c, c, r, Color.BLUE); 25 | 26 | shape.setStroke(Color.RED); 27 | shape.setStrokeWidth(2.0); 28 | shape.getStrokeDashArray().addAll(10.0, 5.0); 29 | 30 | Pane root = new Pane(); 31 | root.getChildren().add(shape); 32 | 33 | stage.setScene(new Scene(root, SIZE, SIZE)); 34 | stage.show(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/test/TextTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javafx.application.Application; 4 | import javafx.scene.Scene; 5 | import javafx.scene.control.Button; 6 | import javafx.scene.text.Text; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.layout.VBox; 9 | import javafx.stage.Stage; 10 | import javafx.geometry.Pos; 11 | import javafx.event.EventHandler; 12 | import javafx.event.ActionEvent; 13 | import javafx.scene.paint.Color; 14 | 15 | public class TextTest extends Application { 16 | 17 | @Override 18 | public void start(final Stage stage) { 19 | final StringBuilder sb = new StringBuilder("Text: "); 20 | final String addText = "01234567890123456789012345678901234567890123456789"; 21 | 22 | final Text textNode = new Text(10, 50, sb.toString()); 23 | textNode.setStroke(Color.TRANSPARENT); 24 | 25 | final Label label = new Label("Length:" + sb.length()); 26 | 27 | final Button button = new Button("Add text"); 28 | button.setOnAction(new EventHandler() { 29 | public void handle(ActionEvent event) { 30 | final String len = "Length: " + sb.length(); 31 | System.out.println(len); 32 | label.setText(len); 33 | 34 | textNode.setText(sb.append(addText).toString()); 35 | } 36 | }); 37 | 38 | final VBox vbox = new VBox(10); 39 | vbox.getChildren().add(button); 40 | vbox.getChildren().add(textNode); 41 | vbox.getChildren().add(label); 42 | vbox.setAlignment(Pos.CENTER); 43 | 44 | stage.setScene(new Scene(vbox)); 45 | stage.setWidth(400); 46 | stage.show(); 47 | } 48 | 49 | public static void main(final String[] args) { 50 | Application.launch(args); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/test/WebViewTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | /* ....Show License.... */ 4 | 5 | import javafx.application.Application; 6 | import javafx.beans.value.ObservableValue; 7 | import javafx.event.ActionEvent; 8 | import javafx.event.EventHandler; 9 | import javafx.scene.Parent; 10 | import javafx.scene.Scene; 11 | import javafx.scene.control.Button; 12 | import javafx.scene.control.TextField; 13 | import javafx.scene.layout.HBox; 14 | import javafx.scene.layout.Priority; 15 | import javafx.scene.layout.VBox; 16 | import javafx.scene.web.WebEngine; 17 | import javafx.scene.web.WebView; 18 | import javafx.stage.Stage; 19 | 20 | /** 21 | * A sample that demonstrates a WebView object accessing a web page. 22 | */ 23 | public class WebViewTest extends Application { 24 | 25 | // From http://www.craftymind.com/factory/guimark2/ 26 | 27 | // public static final String DEFAULT_URL = "http://www.craftymind.com/factory/guimark2/HTML5ChartingTest.html"; 28 | // public static final String DEFAULT_URL = "http://www.craftymind.com/factory/guimark2/HTML5TextTest.html"; 29 | 30 | public static final String DEFAULT_URL = "file:///home/bourgesl/libs/marlin/branches/marlin-fx/src/test/resources/GUIMark 2 - HTML5 Vector Test.html"; 31 | 32 | public Parent createContent() { 33 | 34 | WebView webView = new WebView(); 35 | webView.setCache(false); 36 | 37 | final WebEngine webEngine = webView.getEngine(); 38 | webEngine.load(DEFAULT_URL); 39 | 40 | final TextField locationField = new TextField(DEFAULT_URL); 41 | webEngine.locationProperty().addListener((ObservableValue observable, String oldValue, String newValue) -> { 42 | locationField.setText(newValue); 43 | }); 44 | EventHandler goAction = (ActionEvent event) -> { 45 | webEngine.load(locationField.getText().startsWith("http://") 46 | ? locationField.getText() 47 | : "http://" + locationField.getText()); 48 | }; 49 | locationField.setOnAction(goAction); 50 | 51 | Button goButton = new Button("Go"); 52 | goButton.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE); 53 | goButton.setDefaultButton(true); 54 | goButton.setOnAction(goAction); 55 | 56 | // Layout logic 57 | HBox hBox = new HBox(5); 58 | hBox.getChildren().setAll(locationField, goButton); 59 | HBox.setHgrow(locationField, Priority.ALWAYS); 60 | 61 | VBox vBox = new VBox(5); 62 | vBox.getChildren().setAll(hBox, webView); 63 | vBox.setPrefSize(1400, 800); 64 | VBox.setVgrow(webView, Priority.ALWAYS); 65 | return vBox; 66 | } 67 | 68 | @Override 69 | public void start(Stage primaryStage) throws Exception { 70 | primaryStage.setScene(new Scene(createContent())); 71 | primaryStage.show(); 72 | } 73 | 74 | /** 75 | * Java main for when running without JavaFX launcher 76 | */ 77 | public static void main(String[] args) { 78 | launch(args); 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/resources/com/sun/marlin/Version.properties: -------------------------------------------------------------------------------- 1 | version=${project.artifactId}-${project.version} 2 | -------------------------------------------------------------------------------- /src/test/java/test/com/sun/javafx/geom/Path2DGrowTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package test.com.sun.javafx.geom; 27 | 28 | import com.sun.javafx.geom.Path2D; 29 | import org.junit.Test; 30 | 31 | /** 32 | * @test 33 | * @bug 8169294 34 | * @summary Check the growth algorithm (needRoom) in JavaFX Path2D 35 | */ 36 | /* 37 | Before Patch: 38 | - Test(Path2D[0]) --- 39 | testAddMoves[1000000] duration= 16.319813 ms. 40 | testAddLines[1000000] duration= 1685.904265 ms. 41 | testAddQuads[1000000] duration= 6435.015055999999 ms. 42 | testAddCubics[1000000] duration= 14643.259248999999 ms. 43 | testAddMoveAndCloses[1000000] duration= 2269.6810179999998 ms. 44 | 45 | - Test(Path2D) --- 46 | testAddMoves[1000000] duration= 4.645376 ms. 47 | testAddLines[1000000] duration= 1673.896613 ms. 48 | testAddQuads[1000000] duration= 6448.857066 ms. 49 | testAddCubics[1000000] duration= 14679.410602999998 ms. 50 | testAddMoveAndCloses[1000000] duration= 2278.352159 ms. 51 | 52 | After patch: 53 | - Test(Path2D[0]) --- 54 | testAddMoves[1000000] duration= 15.889125 ms. 55 | testAddLines[1000000] duration= 37.788070999999995 ms. 56 | testAddQuads[1000000] duration= 57.228248 ms. 57 | testAddCubics[1000000] duration= 62.25714 ms. 58 | testAddMoveAndCloses[1000000] duration= 41.76611 ms. 59 | 60 | - Test(Path2D) --- 61 | testAddMoves[1000000] duration= 15.857171999999998 ms. 62 | testAddLines[1000000] duration= 28.228354999999997 ms. 63 | testAddQuads[1000000] duration= 38.190948 ms. 64 | testAddCubics[1000000] duration= 52.453748999999995 ms. 65 | testAddMoveAndCloses[1000000] duration= 26.837844 ms. 66 | */ 67 | public class Path2DGrowTest { 68 | 69 | public static final int N = 1000 * 1000; 70 | 71 | private static boolean verbose = false; 72 | private static boolean force = false; 73 | 74 | static void echo(String msg) { 75 | System.out.println(msg); 76 | } 77 | 78 | static void log(String msg) { 79 | if (verbose || force) { 80 | echo(msg); 81 | } 82 | } 83 | 84 | @Test(timeout=10000) 85 | public void testEmptyFloatPaths() { 86 | echo("\n - Test: new Path2D(0) ---"); 87 | test(() -> new Path2D(Path2D.WIND_NON_ZERO, 0)); 88 | } 89 | 90 | @Test(timeout=10000) 91 | public void testFloatPaths() { 92 | echo("\n - Test: new Path2D() ---"); 93 | test(() -> new Path2D()); 94 | } 95 | 96 | interface PathFactory { 97 | Path2D makePath(); 98 | } 99 | 100 | static void test(PathFactory pf) { 101 | long start, end; 102 | 103 | for (int n = 1; n <= N; n *= 10) { 104 | force = (n == N); 105 | 106 | start = System.nanoTime(); 107 | testAddMoves(pf.makePath(), n); 108 | end = System.nanoTime(); 109 | log("testAddMoves[" + n + "] duration= " 110 | + (1e-6 * (end - start)) + " ms."); 111 | 112 | start = System.nanoTime(); 113 | testAddLines(pf.makePath(), n); 114 | end = System.nanoTime(); 115 | log("testAddLines[" + n + "] duration= " 116 | + (1e-6 * (end - start)) + " ms."); 117 | 118 | start = System.nanoTime(); 119 | testAddQuads(pf.makePath(), n); 120 | end = System.nanoTime(); 121 | log("testAddQuads[" + n + "] duration= " 122 | + (1e-6 * (end - start)) + " ms."); 123 | 124 | start = System.nanoTime(); 125 | testAddCubics(pf.makePath(), n); 126 | end = System.nanoTime(); 127 | log("testAddCubics[" + n + "] duration= " 128 | + (1e-6 * (end - start)) + " ms."); 129 | 130 | start = System.nanoTime(); 131 | testAddMoveAndCloses(pf.makePath(), n); 132 | end = System.nanoTime(); 133 | log("testAddMoveAndCloses[" + n + "] duration= " 134 | + (1e-6 * (end - start)) + " ms."); 135 | } 136 | } 137 | 138 | static void addMove(Path2D p2d, int i) { 139 | p2d.moveTo(1.0f * i, 0.5f * i); 140 | } 141 | 142 | static void addLine(Path2D p2d, int i) { 143 | p2d.lineTo(1.1f * i, 2.3f * i); 144 | } 145 | 146 | static void addCubic(Path2D p2d, int i) { 147 | p2d.curveTo(1.1f * i, 1.2f * i, 1.3f * i, 1.4f * i, 1.5f * i, 1.6f * i); 148 | } 149 | 150 | static void addQuad(Path2D p2d, int i) { 151 | p2d.quadTo(1.1f * i, 1.2f * i, 1.3f * i, 1.4f * i); 152 | } 153 | 154 | static void addClose(Path2D p2d) { 155 | p2d.closePath(); 156 | } 157 | 158 | static void testAddMoves(Path2D pathA, int n) { 159 | for (int i = 0; i < n; i++) { 160 | addMove(pathA, i); 161 | } 162 | } 163 | 164 | static void testAddLines(Path2D pathA, int n) { 165 | addMove(pathA, 0); 166 | for (int i = 0; i < n; i++) { 167 | addLine(pathA, i); 168 | } 169 | } 170 | 171 | static void testAddQuads(Path2D pathA, int n) { 172 | addMove(pathA, 0); 173 | for (int i = 0; i < n; i++) { 174 | addQuad(pathA, i); 175 | } 176 | } 177 | 178 | static void testAddCubics(Path2D pathA, int n) { 179 | addMove(pathA, 0); 180 | for (int i = 0; i < n; i++) { 181 | addCubic(pathA, i); 182 | } 183 | } 184 | 185 | static void testAddMoveAndCloses(Path2D pathA, int n) { 186 | for (int i = 0; i < n; i++) { 187 | addMove(pathA, i); 188 | addClose(pathA); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/test/java/test/com/sun/marlin/DashedRectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | */ 23 | package test.com.sun.marlin; 24 | 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | import javafx.application.Application; 29 | import javafx.application.Platform; 30 | import javafx.collections.ObservableList; 31 | import javafx.geometry.Rectangle2D; 32 | import javafx.scene.Group; 33 | import javafx.scene.Scene; 34 | import javafx.scene.SnapshotParameters; 35 | import javafx.scene.image.PixelReader; 36 | import javafx.scene.image.WritableImage; 37 | import javafx.scene.paint.Color; 38 | import javafx.scene.shape.ClosePath; 39 | import javafx.scene.shape.HLineTo; 40 | import javafx.scene.shape.MoveTo; 41 | import javafx.scene.shape.Path; 42 | import javafx.scene.shape.VLineTo; 43 | import javafx.stage.Stage; 44 | 45 | import junit.framework.AssertionFailedError; 46 | import org.junit.AfterClass; 47 | import static org.junit.Assert.assertEquals; 48 | import static org.junit.Assert.fail; 49 | import org.junit.BeforeClass; 50 | import org.junit.Test; 51 | 52 | import test.util.Util; 53 | import static test.util.Util.TIMEOUT; 54 | 55 | /** 56 | * Simple Dashed Rect rendering test 57 | * 58 | * @test 59 | * @summary verify that dashed rectangle is properly rasterized 60 | * @bug 8202743 61 | */ 62 | public class DashedRectTest { 63 | 64 | static final int BLUE_PIXEL = 0xff0000ff; 65 | 66 | final static double DASH_LEN = 3.0; 67 | final static double DASH_PH = 5000.0; 68 | 69 | final static int MAX = 100; 70 | 71 | // Used to launch the application before running any test 72 | private static final CountDownLatch launchLatch = new CountDownLatch(1); 73 | 74 | // Singleton Application instance 75 | static MyApp myApp; 76 | 77 | // Application class. An instance is created and initialized before running 78 | // the first test, and it lives through the execution of all tests. 79 | public static class MyApp extends Application { 80 | 81 | Stage stage = null; 82 | 83 | public MyApp() { 84 | super(); 85 | } 86 | 87 | @Override 88 | public void init() { 89 | DashedRectTest.myApp = this; 90 | } 91 | 92 | @Override 93 | public void start(Stage primaryStage) throws Exception { 94 | this.stage = primaryStage; 95 | 96 | stage.setScene(new Scene(new Group())); 97 | stage.setTitle("DashedRectTest"); 98 | stage.show(); 99 | 100 | launchLatch.countDown(); 101 | } 102 | } 103 | 104 | @BeforeClass 105 | public static void setupOnce() { 106 | // Start the Application 107 | new Thread(() -> Application.launch(MyApp.class, (String[]) null)).start(); 108 | 109 | try { 110 | if (!launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 111 | throw new AssertionFailedError("Timeout waiting for Application to launch"); 112 | } 113 | 114 | } catch (InterruptedException ex) { 115 | AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 116 | err.initCause(ex); 117 | throw err; 118 | } 119 | 120 | assertEquals(0, launchLatch.getCount()); 121 | } 122 | 123 | @AfterClass 124 | public static void teardownOnce() { 125 | Platform.exit(); 126 | } 127 | 128 | @Test(timeout = 10000) 129 | public void TestDashedPath() throws InterruptedException { 130 | 131 | final int size = MAX * 2; 132 | 133 | Util.runAndWait(() -> { 134 | 135 | // Corrupt Marlin Dasher.dash cached array: 136 | final Path path1 = new Path(); 137 | path1.getElements().addAll( 138 | new MoveTo(20, 20), 139 | new HLineTo(70), 140 | new VLineTo(70), 141 | new HLineTo(20), 142 | new ClosePath() 143 | ); 144 | path1.setStroke(Color.RED); 145 | path1.setFill(null); 146 | path1.setStrokeWidth(2); 147 | path1.setStrokeDashOffset(DASH_PH); 148 | 149 | final ObservableList pDashes = path1.getStrokeDashArray(); 150 | pDashes.clear(); 151 | for (int i = 0; i < 100; i++) { 152 | pDashes.add(19.333); 153 | } 154 | 155 | // Create 2nd path shape 156 | final Path path2 = new Path(); 157 | path2.getElements().addAll( 158 | new MoveTo(5, 5), 159 | new HLineTo(MAX), 160 | new VLineTo(MAX), 161 | new HLineTo(5), 162 | new ClosePath() 163 | ); 164 | path2.setFill(null); 165 | path2.setStroke(Color.BLUE); 166 | path2.setStrokeWidth(2); 167 | path2.setStrokeDashOffset(DASH_PH); 168 | path2.getStrokeDashArray().setAll(DASH_LEN); 169 | 170 | Scene scene = new Scene(new Group(path1, path2)); 171 | 172 | myApp.stage.setScene(scene); 173 | 174 | final SnapshotParameters sp = new SnapshotParameters(); 175 | sp.setViewport(new Rectangle2D(0, 0, size, size)); 176 | 177 | final WritableImage img = scene.getRoot().snapshot(sp, new WritableImage(size, size)); 178 | 179 | // saveImage(img, new File("."), "DashedRectTest.png"); 180 | 181 | // Check image on few pixels: 182 | final PixelReader pr = img.getPixelReader(); 183 | 184 | // 10, 5 = blue 185 | checkPixel(pr, 10, 5, BLUE_PIXEL); 186 | }); 187 | } 188 | 189 | private static void checkPixel(final PixelReader pr, 190 | final int x, final int y, 191 | final int expected) { 192 | 193 | final int rgb = pr.getArgb(x, y); 194 | if (rgb != expected) { 195 | fail("bad pixel at (" + x + ", " + y 196 | + ") = " + rgb + " expected: " + expected); 197 | } 198 | } 199 | /* 200 | static void saveImage(final WritableImage image, final File resDirectory, final String imageFileName) throws IOException { 201 | saveImage(SwingFXUtils.fromFXImage(image, null), resDirectory, imageFileName); 202 | } 203 | 204 | static void saveImage(final BufferedImage image, final File resDirectory, final String imageFileName) throws IOException { 205 | final Iterator itWriters = ImageIO.getImageWritersByFormatName("PNG"); 206 | if (itWriters.hasNext()) { 207 | final ImageWriter writer = itWriters.next(); 208 | 209 | final ImageWriteParam writerParams = writer.getDefaultWriteParam(); 210 | writerParams.setProgressiveMode(ImageWriteParam.MODE_DISABLED); 211 | 212 | final File imgFile = new File(resDirectory, imageFileName); 213 | 214 | if (!imgFile.exists() || imgFile.canWrite()) { 215 | System.out.println("saveImage: saving image as PNG [" + imgFile + "]..."); 216 | imgFile.delete(); 217 | 218 | // disable cache in temporary files: 219 | ImageIO.setUseCache(false); 220 | 221 | final long start = System.nanoTime(); 222 | 223 | // PNG uses already buffering: 224 | final ImageOutputStream imgOutStream = ImageIO.createImageOutputStream(new FileOutputStream(imgFile)); 225 | 226 | writer.setOutput(imgOutStream); 227 | try { 228 | writer.write(null, new IIOImage(image, null, null), writerParams); 229 | } finally { 230 | imgOutStream.close(); 231 | 232 | final long time = System.nanoTime() - start; 233 | System.out.println("saveImage: duration= " + (time / 1000000l) + " ms."); 234 | } 235 | } 236 | } 237 | } 238 | */ 239 | } 240 | -------------------------------------------------------------------------------- /src/test/java/test/com/sun/marlin/QPathTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package test.com.sun.marlin; 27 | 28 | import java.util.Locale; 29 | import java.util.concurrent.CountDownLatch; 30 | import java.util.concurrent.TimeUnit; 31 | import java.util.logging.Handler; 32 | import java.util.logging.Level; 33 | import java.util.logging.LogRecord; 34 | import java.util.logging.Logger; 35 | 36 | import javafx.animation.KeyFrame; 37 | import javafx.animation.KeyValue; 38 | import javafx.animation.Timeline; 39 | import javafx.application.Application; 40 | import javafx.application.Platform; 41 | import javafx.beans.property.DoubleProperty; 42 | import javafx.beans.property.SimpleDoubleProperty; 43 | import javafx.geometry.Bounds; 44 | import javafx.scene.Group; 45 | import javafx.scene.Scene; 46 | import javafx.scene.paint.Color; 47 | import javafx.scene.shape.SVGPath; 48 | import javafx.stage.Stage; 49 | import javafx.util.Duration; 50 | 51 | import junit.framework.AssertionFailedError; 52 | import org.junit.AfterClass; 53 | import org.junit.Assert; 54 | import static org.junit.Assert.assertEquals; 55 | import org.junit.BeforeClass; 56 | import org.junit.Test; 57 | 58 | import static test.util.Util.TIMEOUT; 59 | 60 | /** 61 | * @test 62 | * @bug 8170140 63 | * @summary Check the rendering anomaly with MarlinFX renderer 64 | */ 65 | public class QPathTest { 66 | 67 | private final static double SCALE = 2.0; 68 | 69 | private final static long MAX_DURATION = 3000 * 1000 * 1000L; // 3s 70 | 71 | // Used to launch the application before running any test 72 | private static final CountDownLatch launchLatch = new CountDownLatch(1); 73 | 74 | // Singleton Application instance 75 | static MyApp myApp; 76 | 77 | static boolean doChecksFailed = false; 78 | 79 | private static final Logger log; 80 | 81 | static { 82 | Locale.setDefault(Locale.US); 83 | 84 | // initialize j.u.l Looger: 85 | log = Logger.getLogger("prism.marlin"); 86 | log.addHandler(new Handler() { 87 | @Override 88 | public void publish(LogRecord record) { 89 | final Throwable th = record.getThrown(); 90 | // detect any Throwable: 91 | if (th != null) { 92 | System.out.println("Test failed:\n" + record.getMessage()); 93 | th.printStackTrace(System.out); 94 | 95 | doChecksFailed = true; 96 | 97 | throw new RuntimeException("Test failed: ", th); 98 | } 99 | } 100 | 101 | @Override 102 | public void flush() { 103 | } 104 | 105 | @Override 106 | public void close() { 107 | } 108 | }); 109 | 110 | // enable Marlin logging & internal checks: 111 | System.setProperty("prism.marlinrasterizer", "true"); 112 | System.setProperty("prism.marlin.log", "true"); 113 | System.setProperty("prism.marlin.useLogger", "true"); 114 | System.setProperty("prism.marlin.doChecks", "true"); 115 | } 116 | 117 | private CountDownLatch latch = new CountDownLatch(1); 118 | 119 | // Application class. An instance is created and initialized before running 120 | // the first test, and it lives through the execution of all tests. 121 | public static class MyApp extends Application { 122 | 123 | Stage stage = null; 124 | 125 | public MyApp() { 126 | super(); 127 | } 128 | 129 | @Override 130 | public void init() { 131 | QPathTest.myApp = this; 132 | } 133 | 134 | @Override 135 | public void start(Stage primaryStage) throws Exception { 136 | this.stage = primaryStage; 137 | launchLatch.countDown(); 138 | } 139 | } 140 | 141 | @BeforeClass 142 | public static void setupOnce() { 143 | // Start the Application 144 | new Thread(() -> Application.launch(MyApp.class, (String[]) null)).start(); 145 | 146 | try { 147 | if (!launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 148 | throw new AssertionFailedError("Timeout waiting for Application to launch"); 149 | } 150 | } catch (InterruptedException ex) { 151 | AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 152 | err.initCause(ex); 153 | throw err; 154 | } 155 | 156 | assertEquals(0, launchLatch.getCount()); 157 | } 158 | 159 | @AfterClass 160 | public static void teardownOnce() { 161 | Platform.exit(); 162 | } 163 | 164 | @Test(timeout = 10000) 165 | public void TestBug() { 166 | Platform.runLater(() -> { 167 | SVGPath path = new SVGPath(); 168 | String svgpath = readPath(); 169 | path.setContent(svgpath); 170 | 171 | Scene scene = new Scene(new Group(path), 400, 400, Color.WHITE); 172 | myApp.stage.setScene(scene); 173 | myApp.stage.show(); 174 | /* 175 | DoubleProperty rscale = new SimpleDoubleProperty(SCALE); 176 | myApp.stage.renderScaleXProperty().bind(rscale); 177 | myApp.stage.renderScaleYProperty().bind(rscale); 178 | */ 179 | double scw = scene.getWidth(); 180 | double sch = scene.getHeight(); 181 | Bounds pathbounds = path.getBoundsInParent(); 182 | double pathXoff = -pathbounds.getMinX(); 183 | double pathYoff = -pathbounds.getMinY(); 184 | double bounceW = scw - pathbounds.getWidth(); 185 | double bounceH = sch - pathbounds.getHeight(); 186 | double Xrate = (1.0 + Math.random()) / 2.0; 187 | double Yrate = (1.0 + Math.random()) / 2.0; 188 | 189 | DoubleProperty prop = new SimpleDoubleProperty(); 190 | 191 | final long start = System.nanoTime(); 192 | 193 | prop.addListener((Observable) -> { 194 | if (doChecksFailed || System.nanoTime() - start > MAX_DURATION) { 195 | latch.countDown(); 196 | myApp.stage.close(); 197 | } 198 | double v = prop.doubleValue(); 199 | double x = Math.abs((((v * Xrate) % 2.0) - 1.0) * bounceW); 200 | double y = Math.abs((((v * Yrate) % 2.0) - 1.0) * bounceH); 201 | path.setTranslateX(pathXoff + x); 202 | path.setTranslateY(pathYoff + y); 203 | path.setContent(null); 204 | path.setContent(svgpath); 205 | }); 206 | int bignum = 1000000; 207 | KeyValue kv = new KeyValue(prop, bignum); 208 | KeyFrame kf = new KeyFrame(Duration.seconds(bignum), kv); 209 | Timeline t = new Timeline(kf); 210 | t.setCycleCount(Timeline.INDEFINITE); 211 | t.play(); 212 | }); 213 | try { 214 | latch.await(); 215 | } catch (InterruptedException ie) { 216 | Logger.getLogger(QPathTest.class.getName()).log(Level.SEVERE, "interrupted", ie); 217 | } 218 | Assert.assertFalse("DoChecks detected a problem.", doChecksFailed); 219 | } 220 | 221 | static String readPath() { 222 | return "M54.589844,86.230469 C27.929688,86.230469 10.546875,107.714844 10.546875,140.722656 C10.546875,173.925781 " 223 | + "27.734375,195.214844 54.589844,195.214844 C69.433594,195.214844 80.761719,189.062500 87.500000,177.539063 " 224 | + "L89.062500,177.539063 L89.062500,228.515625 L106.054688,228.515625 L106.054688,88.085938 L89.843750,88.085938 " 225 | + "L89.843750,105.664063 L88.281250,105.664063 C82.031250,93.847656 68.945313,86.230469 54.589844,86.230469 Z " 226 | + "M58.398438,180.078125 C39.257813,180.078125 27.929688,165.429688 27.929688,140.722656 C27.929688,116.113281 " 227 | + "39.355469,101.367188 58.496094,101.367188 C77.539063,101.367188 89.550781,116.601563 89.550781,140.722656 " 228 | + "C89.550781,164.941406 77.636719,180.078125 58.398438,180.078125 Z "; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/test/java/test/util/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package test.util; 27 | 28 | import java.io.BufferedReader; 29 | import java.io.BufferedWriter; 30 | import java.io.File; 31 | import java.io.FileReader; 32 | import java.io.FileWriter; 33 | import java.io.IOException; 34 | import java.net.URL; 35 | import java.util.ArrayList; 36 | import java.util.Iterator; 37 | import java.util.List; 38 | import java.util.concurrent.CountDownLatch; 39 | import java.util.concurrent.TimeUnit; 40 | import javafx.application.Platform; 41 | import junit.framework.AssertionFailedError; 42 | import org.junit.Assert; 43 | 44 | /** 45 | * Utility methods for life-cycle testing 46 | */ 47 | public class Util { 48 | 49 | // Test timeout value in milliseconds 50 | public static final int TIMEOUT = 5000; 51 | 52 | private static interface Future { 53 | public abstract boolean await(long timeout, TimeUnit unit); 54 | } 55 | 56 | public static void throwError(Throwable testError) { 57 | if (testError != null) { 58 | if (testError instanceof Error) { 59 | throw (Error)testError; 60 | } else if (testError instanceof RuntimeException) { 61 | throw (RuntimeException)testError; 62 | } else { 63 | AssertionFailedError err = new AssertionFailedError("Unknown exception"); 64 | err.initCause(testError.getCause()); 65 | throw err; 66 | } 67 | } else { 68 | AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 69 | throw err; 70 | } 71 | } 72 | 73 | public static void sleep(long msec) { 74 | try { 75 | Thread.sleep(msec); 76 | } catch (InterruptedException ex) {} 77 | } 78 | 79 | private static Future submit(final Runnable r, final CountDownLatch delayLatch) { 80 | final Throwable[] testError = new Throwable[1]; 81 | final CountDownLatch latch = new CountDownLatch(1); 82 | 83 | Platform.runLater(() -> { 84 | try { 85 | if (delayLatch != null) { 86 | delayLatch.await(); 87 | } 88 | r.run(); 89 | } catch (Throwable th) { 90 | testError[0] = th; 91 | } finally { 92 | latch.countDown(); 93 | } 94 | }); 95 | 96 | Future future = (timeout, unit) -> { 97 | try { 98 | if (!latch.await(timeout, unit)) { 99 | return false; 100 | } 101 | } catch (InterruptedException ex) { 102 | AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 103 | err.initCause(ex); 104 | throw err; 105 | } 106 | 107 | if (testError[0] != null) { 108 | if (testError[0] instanceof Error) { 109 | throw (Error)testError[0]; 110 | } else if (testError[0] instanceof RuntimeException) { 111 | throw (RuntimeException)testError[0]; 112 | } else { 113 | AssertionFailedError err = new AssertionFailedError("Unknown execution exception"); 114 | err.initCause(testError[0].getCause()); 115 | throw err; 116 | } 117 | } 118 | 119 | return true; 120 | }; 121 | 122 | return future; 123 | } 124 | 125 | public static void runAndWait(Runnable... runnables) { 126 | runAndWait(false, runnables); 127 | } 128 | 129 | public static void runAndWait(boolean delay, Runnable... runnables) { 130 | List futures = new ArrayList(runnables.length); 131 | int i = 0; 132 | CountDownLatch delayLatch = delay ? new CountDownLatch(1) : null; 133 | for (Runnable r : runnables) { 134 | futures.add(submit(r, delayLatch)); 135 | } 136 | if (delayLatch != null) { 137 | delayLatch.countDown(); 138 | } 139 | 140 | int count = TIMEOUT / 100; 141 | while (!futures.isEmpty() && count-- > 0) { 142 | Iterator it = futures.iterator(); 143 | while (it.hasNext()) { 144 | Future future = it.next(); 145 | if (future.await(0, TimeUnit.MILLISECONDS)) { 146 | it.remove(); 147 | } 148 | } 149 | if (!futures.isEmpty()) { 150 | Util.sleep(100); 151 | } 152 | } 153 | 154 | if (!futures.isEmpty()) { 155 | throw new AssertionFailedError("Exceeded timeout limit of " + TIMEOUT + " msec"); 156 | } 157 | } 158 | 159 | public static ArrayList createApplicationLaunchCommand( 160 | String testAppName, 161 | String testPldrName, 162 | String testPolicy) throws IOException { 163 | 164 | final String classpath = System.getProperty("java.class.path"); 165 | 166 | /* 167 | * note: the "worker" properties are tied into build.gradle 168 | */ 169 | final String workerJavaCmd = System.getProperty("worker.java.cmd"); 170 | final String workerPatchModuleFile = System.getProperty("worker.patchmodule.file"); 171 | final String workerPatchPolicy = System.getProperty("worker.patch.policy"); 172 | final String workerClassPath = System.getProperty("worker.classpath.file"); 173 | final Boolean workerDebug = Boolean.getBoolean("worker.debug"); 174 | 175 | final ArrayList cmd = new ArrayList<>(30); 176 | 177 | if (workerJavaCmd != null) { 178 | cmd.add(workerJavaCmd); 179 | } else { 180 | cmd.add("java"); 181 | } 182 | 183 | if (workerPatchModuleFile != null) { 184 | cmd.add("@" + workerPatchModuleFile); 185 | } else { 186 | System.out.println("Warning: no worker.patchmodule passed to unit test"); 187 | } 188 | 189 | // This is a "minimum" set, rather than the full @addExports 190 | cmd.add("--add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED"); 191 | 192 | if (workerClassPath != null) { 193 | cmd.add("@" + workerClassPath); 194 | } 195 | 196 | if (testPldrName != null) { 197 | cmd.add("-Djavafx.preloader=" + testPldrName); 198 | } 199 | 200 | if (testPolicy != null) { 201 | 202 | cmd.add("-Djava.security.manager"); 203 | 204 | try { 205 | if (workerPatchPolicy != null) { 206 | // with Jigsaw, we need to create a merged java.policy 207 | // file that contains the permissions for the patchmodule classes 208 | // as well as the permissions needed for this test 209 | 210 | File wpp = new File(workerPatchPolicy); 211 | if (!wpp.exists()) { 212 | throw new RuntimeException("Missing workerPatchPolicy"); 213 | } 214 | 215 | File tempFile = null; 216 | if (workerDebug) { 217 | tempFile = new File(workerPatchPolicy + 218 | "_" + testAppName); 219 | } else { 220 | tempFile = File.createTempFile("java", "policy"); 221 | tempFile.deleteOnExit(); 222 | } 223 | 224 | BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)); 225 | 226 | BufferedReader reader1 = new BufferedReader(new FileReader(wpp)); 227 | URL url = new URL(testPolicy); 228 | BufferedReader reader2 = new BufferedReader(new FileReader(url.getFile())); 229 | 230 | String line = null; 231 | while ((line = reader1.readLine()) != null) { 232 | writer.write(line); 233 | writer.newLine(); 234 | } 235 | while ((line = reader2.readLine()) != null) { 236 | writer.write(line); 237 | writer.newLine(); 238 | } 239 | writer.close(); 240 | cmd.add("-Djava.security.policy=" + 241 | tempFile.getAbsolutePath().replaceAll("\\\\","/")); 242 | } else { 243 | cmd.add("-Djava.security.policy=" + testPolicy); 244 | } 245 | } catch (IOException e) { 246 | throw e; 247 | } 248 | 249 | } 250 | 251 | cmd.add(testAppName); 252 | 253 | if (workerDebug) { 254 | System.err.println("Child cmd is"); 255 | cmd.stream().forEach((s) -> { 256 | System.err.println(" " + s); 257 | }); 258 | System.err.println("Child cmd: end"); 259 | } 260 | 261 | return cmd; 262 | } 263 | } -------------------------------------------------------------------------------- /src/test/resources/GUIMark 2 - HTML5 Vector Test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | GUIMark 2 - HTML5 Vector Test 4 | 5 | 203 | 217 | 218 | 219 | 220 |
221 |
GUIMark - Vector Chart Test
222 |
223 |
Current: 48.73 fps
224 |
225 |
226 | 227 | 228 | 232 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Default Logging Configuration File 3 | # 4 | # You can use a different file by specifying a filename 5 | # with the java.util.logging.config.file system property. 6 | # For example java -Djava.util.logging.config.file=myfile 7 | ############################################################ 8 | 9 | ############################################################ 10 | # Global properties 11 | ############################################################ 12 | 13 | # "handlers" specifies a comma separated list of log Handler 14 | # classes. These handlers will be installed during VM startup. 15 | # Note that these classes must be on the system classpath. 16 | # By default we only configure a ConsoleHandler, which will only 17 | # show messages at the INFO and above levels. 18 | handlers= java.util.logging.ConsoleHandler 19 | 20 | # To also add the FileHandler, use the following line instead. 21 | #handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler 22 | 23 | # Default global logging level. 24 | # This specifies which kinds of events are logged across 25 | # all loggers. For any given facility this global level 26 | # can be overriden by a facility specific level 27 | # Note that the ConsoleHandler also has a separate level 28 | # setting to limit messages printed to the console. 29 | .level= INFO 30 | 31 | ############################################################ 32 | # Handler specific properties. 33 | # Describes specific configuration info for Handlers. 34 | ############################################################ 35 | 36 | # Limit the message that are printed on the console to INFO and above. 37 | java.util.logging.ConsoleHandler.level = INFO 38 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 39 | 40 | # Example to customize the SimpleFormatter output format 41 | java.util.logging.SimpleFormatter.format=%1$tF %1$tT %4$s %3$s %5$s%6$s%n 42 | 43 | ############################################################ 44 | # Facility specific properties. 45 | # Provides extra control for each logger. 46 | ############################################################ 47 | 48 | # For example, set the org.marlin logger: 49 | # org.marlin.level = DEBUG 50 | --------------------------------------------------------------------------------