From bc7087e774f43e5879522d7be49dfe371941af6c Mon Sep 17 00:00:00 2001 From: bourgesl Date: Fri, 29 Oct 2021 00:17:16 +0200 Subject: [PATCH] removed float-variant + cleanup --- pom.xml | 43 - src/main/java/net/jafama/CmnFastMath.java | 2134 ------------ src/main/java/net/jafama/DoubleWrapper.java | 24 - src/main/java/net/jafama/FastMath.java | 2975 ----------------- src/main/java/net/jafama/IntWrapper.java | 24 - src/main/java/net/jafama/LICENSE-2.0.txt | 202 -- src/main/java/net/jafama/NumbersUtils.java | 2660 --------------- src/main/java/net/jafama/README.txt | 290 -- .../sun/java2d/marlin/ArrayCacheByte.java | 5 +- .../sun/java2d/marlin/ArrayCacheDouble.java | 5 +- .../sun/java2d/marlin/ArrayCacheFloat.java | 269 -- .../java/sun/java2d/marlin/ArrayCacheInt.java | 5 +- .../sun/java2d/marlin/ArrayCacheIntClean.java | 5 +- .../java2d/marlin/CollinearSimplifier.java | 39 +- src/main/java/sun/java2d/marlin/Curve.java | 164 +- .../java2d/marlin/DCollinearSimplifier.java | 158 - src/main/java/sun/java2d/marlin/DCurve.java | 264 -- src/main/java/sun/java2d/marlin/DDasher.java | 1136 ------- src/main/java/sun/java2d/marlin/DHelpers.java | 1020 ------ .../java2d/marlin/DMarlinRenderingEngine.java | 78 +- .../sun/java2d/marlin/DPathSimplifier.java | 165 - .../java/sun/java2d/marlin/DRenderer.java | 1578 --------- .../sun/java2d/marlin/DRendererContext.java | 291 -- src/main/java/sun/java2d/marlin/DStroker.java | 1388 -------- .../marlin/DTransformingPathConsumer2D.java | 1389 -------- src/main/java/sun/java2d/marlin/Dasher.java | 367 +- src/main/java/sun/java2d/marlin/Helpers.java | 399 ++- .../sun/java2d/marlin/IRendererContext.java | 36 - .../java/sun/java2d/marlin/MarlinCache.java | 4 +- .../sun/java2d/marlin/MarlinProperties.java | 5 +- .../sun/java2d/marlin/MarlinRenderer.java | 30 - .../java2d/marlin/MarlinRenderingEngine.java | 1227 ------- .../java2d/marlin/MarlinTileGenerator.java | 29 +- .../java/sun/java2d/marlin/MergeSort.java | 1 + .../sun/java2d/marlin/PathSimplifier.java | 46 +- src/main/java/sun/java2d/marlin/Renderer.java | 194 +- .../sun/java2d/marlin/RendererContext.java | 107 +- src/main/java/sun/java2d/marlin/Stroker.java | 529 ++- .../marlin/TransformingPathConsumer2D.java | 488 ++- src/main/java/test/ClipShapeTest.java | 15 +- src/main/java/test/EndlessLoop.java | 6 - .../services/sun.java2d.pipe.RenderingEngine | 1 - .../java2d/marlin}/Version.properties | 0 src/test/java/ClipShapeTest.java | 15 +- 44 files changed, 1235 insertions(+), 18575 deletions(-) delete mode 100644 src/main/java/net/jafama/CmnFastMath.java delete mode 100644 src/main/java/net/jafama/DoubleWrapper.java delete mode 100644 src/main/java/net/jafama/FastMath.java delete mode 100644 src/main/java/net/jafama/IntWrapper.java delete mode 100644 src/main/java/net/jafama/LICENSE-2.0.txt delete mode 100644 src/main/java/net/jafama/NumbersUtils.java delete mode 100644 src/main/java/net/jafama/README.txt delete mode 100644 src/main/java/sun/java2d/marlin/ArrayCacheFloat.java delete mode 100644 src/main/java/sun/java2d/marlin/DCollinearSimplifier.java delete mode 100644 src/main/java/sun/java2d/marlin/DCurve.java delete mode 100644 src/main/java/sun/java2d/marlin/DDasher.java delete mode 100644 src/main/java/sun/java2d/marlin/DHelpers.java delete mode 100644 src/main/java/sun/java2d/marlin/DPathSimplifier.java delete mode 100644 src/main/java/sun/java2d/marlin/DRenderer.java delete mode 100644 src/main/java/sun/java2d/marlin/DRendererContext.java delete mode 100644 src/main/java/sun/java2d/marlin/DStroker.java delete mode 100644 src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java delete mode 100644 src/main/java/sun/java2d/marlin/IRendererContext.java delete mode 100644 src/main/java/sun/java2d/marlin/MarlinRenderer.java delete mode 100644 src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java rename src/main/resources/{org/marlin/pisces => sun/java2d/marlin}/Version.properties (100%) diff --git a/pom.xml b/pom.xml index d6f2957..9c721f3 100644 --- a/pom.xml +++ b/pom.xml @@ -178,27 +178,6 @@ -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar - - integration-test-float - integration-test - - integration-test - verify - - - ${integration.skip} - - **/RunJUnitTest.java - - - -Xms1g -Xmx1g - -Dsun.java2d.renderer.log=true - -Djava.util.logging.config.file=src/test/resources/logging.properties - -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine - -Xbootclasspath/a:${basedir}/target/${project.build.finalName}.jar - -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar - - @@ -223,28 +202,6 @@ -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar - - integration-long-test-float - integration-test - - integration-test - verify - - - ${integration.skip.long} - - **/RunJUnitLongTest.java - - - -Xms1g -Xmx1g - -Dsun.java2d.renderer.log=true - -DClipShapeTest.numTests=5000 - -Djava.util.logging.config.file=src/test/resources/logging.properties - -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine - -Xbootclasspath/a:${basedir}/target/${project.build.finalName}.jar - -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar - - diff --git a/src/main/java/net/jafama/CmnFastMath.java b/src/main/java/net/jafama/CmnFastMath.java deleted file mode 100644 index f819237..0000000 --- a/src/main/java/net/jafama/CmnFastMath.java +++ /dev/null @@ -1,2134 +0,0 @@ -/* - * Copyright 2014-2015 Jeff Hain - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * ============================================================================= - * Notice of fdlibm package this program is partially derived from: - * - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ============================================================================= - */ -package net.jafama; - -/** - * Stuffs for FastMath and StrictFastMath. - */ -abstract class CmnFastMath { - - /* - * For trigonometric functions, use of look-up tables and Taylor-Lagrange formula - * with 4 derivatives (more take longer to compute and don't add much accuracy, - * less require larger tables (which use more memory, take more time to initialize, - * and are slower to access (at least on the machine they were developed on))). - * - * For angles reduction of cos/sin/tan functions: - * - for small values, instead of reducing angles, and then computing the best index - * for look-up tables, we compute this index right away, and use it for reduction, - * - for large values, treatments derived from fdlibm package are used, as done in - * java.lang.Math. They are faster but still "slow", so if you work with - * large numbers and need speed over accuracy for them, you might want to use - * normalizeXXXFast treatments before your function, or modify cos/sin/tan - * so that they call the fast normalization treatments instead of the accurate ones. - * NB: If an angle is huge (like PI*1e20), in double precision format its last digits - * are zeros, which most likely is not the case for the intended value, and doing - * an accurate reduction on a very inaccurate value is most likely pointless. - * But it gives some sort of coherence that could be needed in some cases. - * - * Multiplication on double appears to be about as fast (or not much slower) than call - * to [], and regrouping some doubles in a private class, to use - * index only once, does not seem to speed things up, so: - * - for uniformly tabulated values, to retrieve the parameter corresponding to - * an index, we recompute it rather than using an array to store it, - * - for cos/sin, we recompute derivatives divided by (multiplied by inverse of) - * factorial each time, rather than storing them in arrays. - * - * Lengths of look-up tables are usually of the form 2^n+1, for their values to be - * of the form ( * k/2^n, k in 0 .. 2^n), so that particular values - * (PI/2, etc.) are "exactly" computed, as well as for other reasons. - * - * Tables are put in specific inner classes, to be lazily initialized. - * Always doing strict tables initialization, even if StrictFastMath delegates - * to StrictMath and doesn't use tables, which makes tables initialization a bit - * slower but code simpler. - * Using redefined pure Java treatments during tables initialization, - * instead of Math or StrictMath ones (even asin(double)), can be very slow, - * because class loading is likely not to be optimized. - * - * Most math treatments I could find on the web, including "fast" ones, - * usually take care of special cases (NaN, etc.) at the beginning, and - * then deal with the general case, which adds a useless overhead for the - * general (and common) case. In this class, special cases are only dealt - * with when needed, and if the general case does not already handle them. - */ - - /* - * Regarding strictfp-ness: - * - * Switching from/to strictfp has some overhead, so we try to only - * strictfp-ize when needed (or when clueless). - * Compile-time constants are computed in a FP-strict way, so no need - * to make this whole class strictfp. - */ - - //-------------------------------------------------------------------------- - // CONFIGURATION - //-------------------------------------------------------------------------- - - /* - * FastMath - */ - - static final boolean FM_USE_JDK_MATH = getBooleanProperty("jafama.usejdk", false); - - /** - * Used for both FastMath.log(double) and FastMath.log10(double). - */ - static final boolean FM_USE_REDEFINED_LOG = getBooleanProperty("jafama.fastlog", false); - - static final boolean FM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.fastsqrt", false); - - /** - * Set it to true if FastMath.sqrt(double) is slow - * (more tables, but less calls to FastMath.sqrt(double)). - */ - static final boolean FM_USE_POWTABS_FOR_ASIN = false; - - /* - * StrictFastMath - */ - - static final boolean SFM_USE_JDK_MATH = getBooleanProperty("jafama.strict.usejdk", false); - - /** - * Used for both StrictFastMath.log(double) and StrictFastMath.log10(double). - * True by default because the StrictMath implementations can be slow. - */ - static final boolean SFM_USE_REDEFINED_LOG = getBooleanProperty("jafama.strict.fastlog", true); - - static final boolean SFM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.strict.fastsqrt", false); - - /** - * Set it to true if StrictFastMath.sqrt(double) is slow - * (more tables, but less calls to StrictFastMath.sqrt(double)). - */ - static final boolean SFM_USE_POWTABS_FOR_ASIN = false; - - /* - * Common to FastMath and StrictFastMath. - */ - - /** - * Using two pow tab can just make things barely faster, - * and could relatively hurt in case of cache-misses, - * especially for methods that otherwise wouldn't rely - * on any tab, so we don't use it. - */ - static final boolean USE_TWO_POW_TAB = false; - - /** - * Because on some architectures, some casts can be slow, - * especially for large values. - * Might make things a bit slower for latest architectures, - * but not as much as it makes them faster for older ones. - */ - static final boolean ANTI_SLOW_CASTS = true; - - /** - * If some methods get JIT-optimized, they might crash - * if they contain "(var == xxx)" with var being NaN - * (can happen with Java 6u29). - * - * The crash does not happen if we replace "==" with "<" or ">". - * - * Only the code that has been observed to trigger the bug - * has been modified. - */ - static final boolean ANTI_JIT_OPTIM_CRASH_ON_NAN = true; - - //-------------------------------------------------------------------------- - // GENERAL CONSTANTS - //-------------------------------------------------------------------------- - - /** - * Closest double approximation of e. - */ - public static final double E = Math.E; - - /** - * Closest double approximation of pi, which is inferior to mathematical pi: - * pi ~= 3.14159265358979323846... - * PI ~= 3.141592653589793 - */ - public static final double PI = Math.PI; - - /** - * High double approximation of pi, which is further from pi - * than the low approximation PI: - * pi ~= 3.14159265358979323846... - * PI ~= 3.141592653589793 - * PI_SUP ~= 3.1415926535897936 - */ - public static final double PI_SUP = Double.longBitsToDouble(Double.doubleToRawLongBits(Math.PI)+1); - - static final double ONE_DIV_F2 = 1/2.0; - static final double ONE_DIV_F3 = 1/6.0; - static final double ONE_DIV_F4 = 1/24.0; - - static final float TWO_POW_23_F = (float)NumbersUtils.twoPow(23); - - static final double TWO_POW_24 = NumbersUtils.twoPow(24); - private static final double TWO_POW_N24 = NumbersUtils.twoPow(-24); - - static final double TWO_POW_26 = NumbersUtils.twoPow(26); - static final double TWO_POW_N26 = NumbersUtils.twoPow(-26); - - // First double value (from zero) such as (value+-1/value == value). - static final double TWO_POW_27 = NumbersUtils.twoPow(27); - static final double TWO_POW_N27 = NumbersUtils.twoPow(-27); - - static final double TWO_POW_N28 = NumbersUtils.twoPow(-28); - - static final double TWO_POW_52 = NumbersUtils.twoPow(52); - - static final double TWO_POW_N55 = NumbersUtils.twoPow(-55); - - static final double TWO_POW_66 = NumbersUtils.twoPow(66); - - static final double TWO_POW_512 = NumbersUtils.twoPow(512); - static final double TWO_POW_N512 = NumbersUtils.twoPow(-512); - - /** - * Double.MIN_NORMAL since Java 6. - */ - static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308 - - // Not storing float/double mantissa size in constants, - // for 23 and 52 are shorter to read and more - // bitwise-explicit than some constant's name. - - static final int MIN_DOUBLE_EXPONENT = -1074; - static final int MIN_DOUBLE_NORMAL_EXPONENT = -1022; - static final int MAX_DOUBLE_EXPONENT = 1023; - - static final int MIN_FLOAT_NORMAL_EXPONENT = -126; - static final int MAX_FLOAT_EXPONENT = 127; - - private static final double SQRT_2 = StrictMath.sqrt(2.0); - - static final double LOG_2 = StrictMath.log(2.0); - static final double LOG_TWO_POW_27 = StrictMath.log(TWO_POW_27); - static final double LOG_DOUBLE_MAX_VALUE = StrictMath.log(Double.MAX_VALUE); - - static final double INV_LOG_10 = 1.0/StrictMath.log(10.0); - - static final double DOUBLE_BEFORE_60 = Double.longBitsToDouble(Double.doubleToRawLongBits(60.0)-1); - - //-------------------------------------------------------------------------- - // CONSTANTS FOR NORMALIZATIONS - //-------------------------------------------------------------------------- - - /** - * Table of constants for 1/(PI/2), 282 Hex digits (enough for normalizing doubles). - * 1/(PI/2) approximation = sum of TWO_OVER_PI_TAB[i]*2^(-24*(i+1)). - * - * double and not int, to avoid int-to-double cast during computations. - */ - private static final double TWO_OVER_PI_TAB[] = { - 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, - 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, - 0x424DD2, 0xe00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, - 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, - 0x3991d6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, - 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, - 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, - 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, - 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, - 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, - 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B}; - - /* - * Constants for PI/2. Only the 23 most significant bits of each mantissa are used. - * 2*PI approximation = sum of TWOPI_TAB. - */ - private static final double PIO2_TAB0 = Double.longBitsToDouble(0x3FF921FB40000000L); - private static final double PIO2_TAB1 = Double.longBitsToDouble(0x3E74442D00000000L); - private static final double PIO2_TAB2 = Double.longBitsToDouble(0x3CF8469880000000L); - private static final double PIO2_TAB3 = Double.longBitsToDouble(0x3B78CC5160000000L); - private static final double PIO2_TAB4 = Double.longBitsToDouble(0x39F01B8380000000L); - private static final double PIO2_TAB5 = Double.longBitsToDouble(0x387A252040000000L); - - static final double PIO2_INV = Double.longBitsToDouble(0x3FE45F306DC9C883L); // 6.36619772367581382433e-01 53 bits of 2/pi - static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2 - static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI - static final double PI_INV = PIO2_INV/2; - static final double PI_HI = 2*PIO2_HI; - static final double PI_LO = 2*PIO2_LO; - static final double TWOPI_INV = PIO2_INV/4; - static final double TWOPI_HI = 4*PIO2_HI; - static final double TWOPI_LO = 4*PIO2_LO; - - /** - * Bit = 0 where quadrant is encoded in remainder bits. - */ - private static final long QUADRANT_BITS_0_MASK = 0xCFFFFFFFFFFFFFFFL; - - /** - * Remainder bits where quadrant is encoded, 0 elsewhere. - */ - private static final long QUADRANT_PLACE_BITS = 0x3000000000000000L; - - /** - * fdlibm uses 2^19*PI/2 here. - * With 2^18*PI/2 we would be more accurate, for example when normalizing - * 822245.903631403, which is close to 2^19*PI/2, but we are still in - * our accuracy tolerance with fdlibm's value (but not 2^20*PI/2) so we - * stick to it, to help being faster than (Strict)Math for values in - * [2^18*PI/2,2^19*PI/2]. - * - * For tests, can use a smaller value, for heavy remainder - * not to only be used with huge values. - */ - static final double NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2 = StrictMath.pow(2.0,19.0)*(Math.PI/2); - - /** - * 2*Math.PI, normalized into [-PI,PI], as returned by - * StrictMath.asin(StrictMath.sin(2*Math.PI)) - * (asin behaves as identity for this). - * - * NB: NumbersUtils.minus2PI(2*Math.PI) returns -2.449293598153844E-16, - * which is different due to not using an accurate enough definition of PI. - */ - static final double TWO_MATH_PI_IN_MINUS_PI_PI = -2.4492935982947064E-16; - - //-------------------------------------------------------------------------- - // CONSTANTS AND TABLES FOR SIN AND COS - //-------------------------------------------------------------------------- - - static final int SIN_COS_TABS_SIZE = (1<>9) / SIN_COS_INDEXER) * 0.99; - - //-------------------------------------------------------------------------- - // CONSTANTS AND TABLES FOR TAN - //-------------------------------------------------------------------------- - - // We use the following formula: - // 1) tan(-x) = -tan(x) - // 2) tan(x) = 1/tan(PI/2-x) - // ---> we only have to compute tan(x) on [0,A] with PI/4<=A= 45deg, and supposed to be >= 51.4deg, as fdlibm code is not - * supposed to work with values inferior to that (51.4deg is about - * (PI/2-Double.longBitsToDouble(0x3FE5942800000000L))). - */ - static final double TAN_MAX_VALUE_FOR_TABS = StrictMath.toRadians(77.0); - - static final int TAN_TABS_SIZE = (int)((TAN_MAX_VALUE_FOR_TABS/(Math.PI/2)) * (TAN_VIRTUAL_TABS_SIZE-1)) + 1; - static final double TAN_DELTA_HI = PIO2_HI/(TAN_VIRTUAL_TABS_SIZE-1); - static final double TAN_DELTA_LO = PIO2_LO/(TAN_VIRTUAL_TABS_SIZE-1); - static final double TAN_INDEXER = 1/(TAN_DELTA_HI+TAN_DELTA_LO); - - static final class MyTTan { - static final double[] tanTab = new double[TAN_TABS_SIZE]; - static final double[] tanDer1DivF1Tab = new double[TAN_TABS_SIZE]; - static final double[] tanDer2DivF2Tab = new double[TAN_TABS_SIZE]; - static final double[] tanDer3DivF3Tab = new double[TAN_TABS_SIZE]; - static final double[] tanDer4DivF4Tab = new double[TAN_TABS_SIZE]; - static { - init(); - } - private static strictfp void init() { - for (int i=0;i>9) / TAN_INDEXER) * 0.99); - - //-------------------------------------------------------------------------- - // CONSTANTS AND TABLES FOR ACOS, ASIN - //-------------------------------------------------------------------------- - - // We use the following formula: - // 1) acos(x) = PI/2 - asin(x) - // 2) asin(-x) = -asin(x) - // ---> we only have to compute asin(x) on [0,1]. - // For values not close to +-1, we use look-up tables; - // for values near +-1, we use code derived from fdlibm. - - /** - * Supposed to be >= sin(77.2deg), as fdlibm code is supposed to work with values > 0.975, - * but seems to work well enough as long as value >= sin(25deg). - */ - static final double ASIN_MAX_VALUE_FOR_TABS = StrictMath.sin(StrictMath.toRadians(73.0)); - - static final int ASIN_TABS_SIZE = (1< we only have to compute atan(x) on [0,+Infinity[. - // For values corresponding to angles not close to +-PI/2, we use look-up tables; - // for values corresponding to angles near +-PI/2, we use code derived from fdlibm. - - /** - * Supposed to be >= tan(67.7deg), as fdlibm code is supposed to work with values > 2.4375. - */ - static final double ATAN_MAX_VALUE_FOR_TABS = StrictMath.tan(StrictMath.toRadians(74.0)); - - static final int ATAN_TABS_SIZE = (1<>SQRT_LO_BITS)); - for (int i=1;i>CBRT_LO_BITS)); - for (int i=1;i= MIN_DOUBLE_EXPONENT) { - if (power <= MAX_DOUBLE_EXPONENT) { // Normal or subnormal. - return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; - } else { // Overflow. - return Double.POSITIVE_INFINITY; - } - } else { // Underflow. - return 0.0; - } - } else { - return NumbersUtils.twoPow(power); - } - } - - /** - * @param value An int value. - * @return value*value. - */ - public static int pow2(int value) { - return value*value; - } - - /** - * @param value A long value. - * @return value*value. - */ - public static long pow2(long value) { - return value*value; - } - - /** - * @param value An int value. - * @return value*value*value. - */ - public static int pow3(int value) { - return value*value*value; - } - - /** - * @param value A long value. - * @return value*value*value. - */ - public static long pow3(long value) { - return value*value*value; - } - - /* - * absolute values - */ - - /** - * @param value An int value. - * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE. - */ - public static int abs(int value) { - if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) { - return Math.abs(value); - } - return NumbersUtils.abs(value); - } - - /** - * @param value A long value. - * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE. - */ - public static long abs(long value) { - if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) { - return Math.abs(value); - } - return NumbersUtils.abs(value); - } - - /* - * close values - */ - - /** - * @param value A long value. - * @return The specified value as int. - * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int toIntExact(long value) { - return NumbersUtils.asInt(value); - } - - /** - * @param value A long value. - * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int toInt(long value) { - return NumbersUtils.toInt(value); - } - - /* - * ranges - */ - - /** - * @param min An int value. - * @param max An int value. - * @param value An int value. - * @return minValue if value < minValue, maxValue if value > maxValue, value otherwise. - */ - public static int toRange(int min, int max, int value) { - return NumbersUtils.toRange(min, max, value); - } - - /** - * @param min A long value. - * @param max A long value. - * @param value A long value. - * @return min if value < min, max if value > max, value otherwise. - */ - public static long toRange(long min, long max, long value) { - return NumbersUtils.toRange(min, max, value); - } - - /* - * unary operators (increment,decrement,negate) - */ - - /** - * @param value An int value. - * @return The argument incremented by one. - * @throws ArithmeticException if the mathematical result - * is not in int range. - */ - public static int incrementExact(int value) { - if (value == Integer.MAX_VALUE) { - throw new ArithmeticException("integer overflow"); - } - return value + 1; - } - - /** - * @param value A long value. - * @return The argument incremented by one. - * @throws ArithmeticException if the mathematical result - * is not in long range. - */ - public static long incrementExact(long value) { - if (value == Long.MAX_VALUE) { - throw new ArithmeticException("long overflow"); - } - return value + 1L; - } - - /** - * @param value An int value. - * @return The argument incremented by one, or the argument - * if the mathematical result is not in int range. - */ - public static int incrementBounded(int value) { - if (value == Integer.MAX_VALUE) { - return value; - } - return value + 1; - } - - /** - * @param value A long value. - * @return The argument incremented by one, or the argument - * if the mathematical result is not in long range. - */ - public static long incrementBounded(long value) { - if (value == Long.MAX_VALUE) { - return value; - } - return value + 1L; - } - - /** - * @param value An int value. - * @return The argument decremented by one. - * @throws ArithmeticException if the mathematical result - * is not in int range. - */ - public static int decrementExact(int value) { - if (value == Integer.MIN_VALUE) { - throw new ArithmeticException("integer overflow"); - } - return value - 1; - } - - /** - * @param value A long value. - * @return The argument decremented by one. - * @throws ArithmeticException if the mathematical result - * is not in long range. - */ - public static long decrementExact(long value) { - if (value == Long.MIN_VALUE) { - throw new ArithmeticException("long overflow"); - } - return value - 1L; - } - - /** - * @param value An int value. - * @return The argument decremented by one, or the argument - * if the mathematical result is not in int range. - */ - public static int decrementBounded(int value) { - if (value == Integer.MIN_VALUE) { - return value; - } - return value - 1; - } - - /** - * @param value A long value. - * @return The argument decremented by one, or the argument - * if the mathematical result is not in long range. - */ - public static long decrementBounded(long value) { - if (value == Long.MIN_VALUE) { - return value; - } - return value - 1L; - } - - /** - * @param value An int value. - * @return The argument negated. - * @throws ArithmeticException if the mathematical result - * is not in int range. - */ - public static int negateExact(int value) { - if (value == Integer.MIN_VALUE) { - throw new ArithmeticException("integer overflow"); - } - return -value; - } - - /** - * @param value A long value. - * @return The argument negated. - * @throws ArithmeticException if the mathematical result - * is not in long range. - */ - public static long negateExact(long value) { - if (value == Long.MIN_VALUE) { - throw new ArithmeticException("long overflow"); - } - return -value; - } - - /** - * @param value An int value. - * @return The argument negated, or Integer.MAX_VALUE - * if the argument is Integer.MIN_VALUE. - */ - public static int negateBounded(int value) { - if (value == Integer.MIN_VALUE) { - return Integer.MAX_VALUE; - } - return -value; - } - - /** - * @param value A long value. - * @return The argument negated, or Long.MAX_VALUE - * if the argument is Long.MIN_VALUE. - */ - public static long negateBounded(long value) { - if (value == Long.MIN_VALUE) { - return Long.MAX_VALUE; - } - return -value; - } - - /* - * binary operators (+,-,*) - */ - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a+b. - * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int addExact(int a, int b) { - return NumbersUtils.plusExact(a, b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a+b. - * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long addExact(long a, long b) { - return NumbersUtils.plusExact(a, b); - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b. - */ - public static int addBounded(int a, int b) { - return NumbersUtils.plusBounded(a, b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b. - */ - public static long addBounded(long a, long b) { - return NumbersUtils.plusBounded(a, b); - } - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a-b. - * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int subtractExact(int a, int b) { - return NumbersUtils.minusExact(a, b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a-b. - * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long subtractExact(long a, long b) { - return NumbersUtils.minusExact(a, b); - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b. - */ - public static int subtractBounded(int a, int b) { - return NumbersUtils.minusBounded(a, b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b. - */ - public static long subtractBounded(long a, long b) { - return NumbersUtils.minusBounded(a, b); - } - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a*b. - * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int multiplyExact(int a, int b) { - return NumbersUtils.timesExact(a, b); - } - - /** - * @param a A long value. - * @param b An int value. - * @return The mathematical result of a*b. - * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long multiplyExact(long a, int b) { - return NumbersUtils.timesExact(a, (long) b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a*b. - * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long multiplyExact(long a, long b) { - return NumbersUtils.timesExact(a, b); - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b. - */ - public static int multiplyBounded(int a, int b) { - return NumbersUtils.timesBounded(a, b); - } - - /** - * @param a A long value. - * @param b An int value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. - */ - public static long multiplyBounded(long a, int b) { - return NumbersUtils.timesBounded(a, (long) b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. - */ - public static long multiplyBounded(long a, long b) { - return NumbersUtils.timesBounded(a, b); - } - - /** - * @param x An int value. - * @param y An int value. - * @return The mathematical product as a long. - */ - public static long multiplyFull(int x, int y) { - return ((long) x) * ((long) y); - } - - /** - * @param x A long value. - * @param y A long value. - * @return The most significant 64 bits of the 128-bit product of two 64-bit factors. - */ - public static long multiplyHigh(long x, long y) { - if ((x|y) < 0) { - // Use technique from section 8-2 of Henry S. Warren, Jr., - // Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174. - long x1 = (x >> 32); - long y1 = (y >> 32); - long x2 = (x & 0xFFFFFFFFL); - long y2 = (y & 0xFFFFFFFFL); - long z2 = x2 * y2; - long t = x1 * y2 + (z2 >>> 32); - long z1 = (t & 0xFFFFFFFFL) + x2 * y1; - long z0 = (t >> 32); - return x1 * y1 + z0 + (z1 >> 32); - } else { - // Use Karatsuba technique with two base 2^32 digits. - long x1 = (x >>> 32); - long y1 = (y >>> 32); - long x2 = (x & 0xFFFFFFFFL); - long y2 = (y & 0xFFFFFFFFL); - long A = x1 * y1; - long B = x2 * y2; - long C = (x1 + x2) * (y1 + y2); - long K = C - A - B; - return (((B >>> 32) + K) >>> 32) + A; - } - } - - /* - * binary operators (/,%) - */ - - /** - * Returns the largest int <= dividend/divisor. - * - * Unlike "/" operator, which rounds towards 0, this division - * rounds towards -Infinity (which give different result - * when the exact result is negative). - * - * @param x The dividend. - * @param y The divisor. - * @return The largest int <= dividend/divisor, unless dividend is - * Integer.MIN_VALUE and divisor is -1, in which case - * Integer.MIN_VALUE is returned. - * @throws ArithmeticException if the divisor is zero. - */ - public static int floorDiv(int x, int y) { - int r = x / y; - // If the signs are different and modulo not zero, rounding down. - if (((x ^ y) < 0) && ((r * y) != x)) { - r--; - } - return r; - } - - /** - * Returns the largest long <= dividend/divisor. - * - * Unlike "/" operator, which rounds towards 0, this division - * rounds towards -Infinity (which give different result - * when the exact result is negative). - * - * @param x The dividend. - * @param y The divisor. - * @return The largest long <= dividend/divisor, unless dividend is - * Long.MIN_VALUE and divisor is -1, in which case - * Long.MIN_VALUE is returned. - * @throws ArithmeticException if the divisor is zero. - */ - public static long floorDiv(long x, int y) { - return floorDiv(x, (long) y); - } - - /** - * Returns the largest long <= dividend/divisor. - * - * Unlike "/" operator, which rounds towards 0, this division - * rounds towards -Infinity (which give different result - * when the exact result is negative). - * - * @param x The dividend. - * @param y The divisor. - * @return The largest long <= dividend/divisor, unless dividend is - * Long.MIN_VALUE and divisor is -1, in which case - * Long.MIN_VALUE is returned. - * @throws ArithmeticException if the divisor is zero. - */ - public static long floorDiv(long x, long y) { - long r = x / y; - // If the signs are different and modulo not zero, rounding down. - if (((x ^ y) < 0) && ((r * y) != x)) { - r--; - } - return r; - } - - /** - * Returns the floor modulus, which is "x - floorDiv(x,y) * y", - * has the same sign as y, and is in ]-abs(y),abs(y)[. - * - * The relationship between floorMod and floorDiv is the same - * than between "%" and "/". - * - * @param x The dividend. - * @param y The divisor. - * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". - * @throws ArithmeticException if the divisor is zero. - */ - public static int floorMod(int x, int y) { - return x - floorDiv(x, y) * y; - } - - /** - * Returns the floor modulus, which is "x - floorDiv(x,y) * y", - * has the same sign as y, and is in ]-abs(y),abs(y)[. - * - * The relationship between floorMod and floorDiv is the same - * than between "%" and "/". - * - * @param x The dividend. - * @param y The divisor. - * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". - * @throws ArithmeticException if the divisor is zero. - */ - public static int floorMod(long x, int y) { - // No overflow so can cast. - return (int) (x - floorDiv(x,y) * y); - } - - /** - * Returns the floor modulus, which is "x - floorDiv(x,y) * y", - * has the same sign as y, and is in ]-abs(y),abs(y)[. - * - * The relationship between floorMod and floorDiv is the same - * than between "%" and "/". - * - * @param x The dividend. - * @param y The divisor. - * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". - * @throws ArithmeticException if the divisor is zero. - */ - public static long floorMod(long x, long y) { - return x - floorDiv(x, y) * y; - } - - /* - * Non-redefined Math public values and treatments. - */ - - public static int min(int a, int b) { - return Math.min(a,b); - } - - public static long min(long a, long b) { - return Math.min(a,b); - } - - public static int max(int a, int b) { - return Math.max(a,b); - } - - public static long max(long a, long b) { - return Math.max(a,b); - } - - //-------------------------------------------------------------------------- - // PACKAGE-PRIVATE METHODS - //-------------------------------------------------------------------------- - - /** - * @param power Must be in normal values range. - */ - static double twoPowNormal(int power) { - if (USE_TWO_POW_TAB) { - return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; - } else { - return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); - } - } - - /** - * @param power Must be in normal or subnormal values range. - */ - static double twoPowNormalOrSubnormal(int power) { - if (USE_TWO_POW_TAB) { - return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; - } else { - if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal. - return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT))); - } else { // Normal. - return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); - } - } - } - - static double atan2_pinf_yyy(double y) { - if (y == Double.POSITIVE_INFINITY) { - return Math.PI/4; - } else if (y == Double.NEGATIVE_INFINITY) { - return -Math.PI/4; - } else if (y > 0.0) { - return 0.0; - } else if (y < 0.0) { - return -0.0; - } else { - return Double.NaN; - } - } - - static double atan2_ninf_yyy(double y) { - if (y == Double.POSITIVE_INFINITY) { - return 3*Math.PI/4; - } else if (y == Double.NEGATIVE_INFINITY) { - return -3*Math.PI/4; - } else if (y > 0.0) { - return Math.PI; - } else if (y < 0.0) { - return -Math.PI; - } else { - return Double.NaN; - } - } - - static double atan2_yyy_zeroOrNaN(double y, double x) { - if (x == 0.0) { - if (y == 0.0) { - if (signFromBit_antiCyclic(x) < 0) { - // x is -0.0 - return signFromBit_antiCyclic(y) * Math.PI; - } else { - // +-0.0 - return y; - } - } - if (y > 0.0) { - return Math.PI/2; - } else if (y < 0.0) { - return -Math.PI/2; - } else { - return Double.NaN; - } - } else { - return Double.NaN; - } - } - - /** - * At least one of the arguments must be NaN. - */ - static double hypot_NaN(double xAbs, double yAbs) { - if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY)) { - return Double.POSITIVE_INFINITY; - } else { - return Double.NaN; - } - } - - /** - * At least one of the arguments must be NaN. - */ - static double hypot_NaN(double xAbs, double yAbs, double zAbs) { - if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY) || (zAbs == Double.POSITIVE_INFINITY)) { - return Double.POSITIVE_INFINITY; - } else { - return Double.NaN; - } - } - - /* - * - */ - - /** - * @param remainder Must have 1 for 2nd and 3rd exponent bits, which is the - * case for heavyRemPiO2 remainders (their absolute values are >= - * Double.longBitsToDouble(0x3000000000000000L) - * = 1.727233711018889E-77, and even if they were not, turning these - * bits from 0 to 1 on decoding would not change the absolute error - * much), and also works for +-Infinity or NaN encoding. - * @param quadrant Must be in [0,3]. - * @return Bits holding remainder, and quadrant instead of - * reamainder's 2nd and 3rd exponent bits. - */ - static long encodeRemainderAndQuadrant(double remainder, int quadrant) { - final long bits = Double.doubleToRawLongBits(remainder); - return (bits&QUADRANT_BITS_0_MASK)|(((long)quadrant)<<60); - } - - static double decodeRemainder(long bits) { - return Double.longBitsToDouble((bits&QUADRANT_BITS_0_MASK)|QUADRANT_PLACE_BITS); - } - - static int decodeQuadrant(long bits) { - return ((int)(bits>>60))&3; - } - - /* - * JDK-based remainders. - * Since a strict one for (% (PI/2)) is needed for heavyRemainderPiO2, - * we need it in this class. - * Then, for homogeneity, we put them all in this class. - * Then, to avoid code duplication for these slow-anyway methods, - * we just stick with strict versions, for both FastMath and StrictFastMath. - */ - - /** - * @param angle Angle, in radians. - * @return Remainder of (angle % (2*PI)), in [-PI,PI]. - */ - static strictfp double jdkRemainderTwoPi(double angle) { - final double sin = StrictMath.sin(angle); - final double cos = StrictMath.cos(angle); - return StrictMath.atan2(sin, cos); - } - - /** - * @param angle Angle, in radians. - * @return Remainder of (angle % PI), in [-PI/2,PI/2]. - */ - static strictfp double jdkRemainderPi(double angle) { - final double sin = StrictMath.sin(angle); - final double cos = StrictMath.cos(angle); - /* - * Making sure atan2's result ends up in [-PI/2,PI/2], - * i.e. has maximum accuracy. - */ - return StrictMath.atan2(sin, Math.abs(cos)); - } - - /** - * @param angle Angle, in radians. - * @return Bits of double corresponding to remainder of (angle % (PI/2)), - * in [-PI/4,PI/4], with quadrant encoded in exponent bits. - */ - static strictfp long jdkRemainderPiO2(double angle, boolean negateRem) { - final double sin = StrictMath.sin(angle); - final double cos = StrictMath.cos(angle); - - /* - * Computing quadrant first, and then computing - * atan2, to make sure its result ends up in [-PI/4,PI/4], - * i.e. has maximum accuracy. - */ - - final int q; - final double sinForAtan2; - final double cosForAtan2; - if (cos >= (SQRT_2/2)) { - // [-PI/4,PI/4] - q = 0; - sinForAtan2 = sin; - cosForAtan2 = cos; - } else if (cos <= -(SQRT_2/2)) { - // [3*PI/4,5*PI/4] - q = 2; - sinForAtan2 = -sin; - cosForAtan2 = -cos; - } else if (sin > 0.0) { - // [PI/4,3*PI/4] - q = 1; - sinForAtan2 = -cos; - cosForAtan2 = sin; - } else { - // [5*PI/4,7*PI/4] - q = 3; - sinForAtan2 = cos; - cosForAtan2 = -sin; - } - - double fw = StrictMath.atan2(sinForAtan2, cosForAtan2); - - return encodeRemainderAndQuadrant(negateRem ? -fw : fw, q); - } - - /* - * Our remainders implementations. - */ - - /** - * @param angle Angle, in radians. Must not be NaN nor +-Infinity. - * @return Remainder of (angle % (2*PI)), in [-PI,PI]. - */ - static strictfp double heavyRemainderTwoPi(double angle) { - final long remAndQuad = heavyRemainderPiO2(angle, false); - final double rem = decodeRemainder(remAndQuad); - final int q = decodeQuadrant(remAndQuad); - if (q == 0) { - return rem; - } else if (q == 1) { - return (rem + PIO2_LO) + PIO2_HI; - } else if (q == 2) { - if (rem < 0.0) { - return (rem + PI_LO) + PI_HI; - } else { - return (rem - PI_LO) - PI_HI; - } - } else { - return (rem - PIO2_LO) - PIO2_HI; - } - } - - /** - * @param angle Angle, in radians. Must not be NaN nor +-Infinity. - * @return Remainder of (angle % PI), in [-PI/2,PI/2]. - */ - static strictfp double heavyRemainderPi(double angle) { - final long remAndQuad = heavyRemainderPiO2(angle, false); - final double rem = decodeRemainder(remAndQuad); - final int q = decodeQuadrant(remAndQuad); - if ((q&1) != 0) { - // q is 1 or 3 - if (rem < 0.0) { - return (rem + PIO2_LO) + PIO2_HI; - } else { - return (rem - PIO2_LO) - PIO2_HI; - } - } - return rem; - } - - /** - * Remainder using an accurate definition of PI. - * Derived from a fdlibm treatment called __kernel_rem_pio2. - * - * Not defining a non-strictfp version for FastMath, to avoid duplicating - * its long and messy code, and because it's slow anyway, and should be - * rarely used when speed matters. - * - * @param angle Angle, in radians. Must not be NaN nor +-Infinity. - * @param negateRem True if remainder must be negated before encoded into returned long. - * @return Bits of double corresponding to remainder of (angle % (PI/2)), - * in [-PI/4,PI/4], with quadrant encoded in exponent bits. - */ - static strictfp long heavyRemainderPiO2(double angle, boolean negateRem) { - - /* - * fdlibm treatments unrolled, to avoid garbage and be OOME-free, - * corresponding to: - * 1) initial jk = 4 (precision = 3 = 64 bits (extended)), - * which is more accurate than using precision = 2 - * (53 bits, double), even though we work with doubles - * and use strictfp! - * 2) max lengths of 8 for f[], 6 for q[], fq[] and iq[]. - * 3) at most one recomputation (one goto). - * These limitations were experimentally found to - * be sufficient for billions of random doubles - * of random magnitudes. - * For the rare cases that our unrolled treatments can't handle, - * we fall back to a JDK-based implementation. - */ - - int n,i,j,ih; - double fw; - - /* - * Turning angle into 24-bits integer chunks. - * Done outside __kernel_rem_pio2, but we factor it inside our method. - */ - - // Reworking exponent to have a value < 2^24. - final long lx = Double.doubleToRawLongBits(angle); - final long exp = ((lx>>52)&0x7FF) - (1023+23); - double z = Double.longBitsToDouble(lx - (exp<<52)); - - double x0 = (double)(int)z; - z = (z-x0)*TWO_POW_24; - double x1 = (double)(int)z; - z = (z-x1)*TWO_POW_24; - double x2 = (double)(int)z; - - final int e0 = (int)exp; - // in [1,3] - final int nx = (x2 == 0.0) ? ((x1 == 0.0) ? 1 : 2) : 3; - - /* - * - */ - - double f0,f1,f2,f3,f4,f5,f6,f7; - double q0,q1,q2,q3,q4,q5; - int iq0,iq1,iq2,iq3,iq4,iq5; - - int jk = 4; - - int jx = nx-1; - int jv = Math.max(0,(e0-3)/24); - // In fdlibm, this is q0, but we prefer to use q0 for q[0]. - int qZero = e0-24*(jv+1); - - j = jv-jx; - if (jx == 0) { - f6 = 0.0; - f5 = 0.0; - f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; - f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; - f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; - f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; - f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0; - - q0 = x0*f0; - q1 = x0*f1; - q2 = x0*f2; - q3 = x0*f3; - q4 = x0*f4; - } else if (jx == 1) { - f6 = 0.0; - f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0; - f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; - f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; - f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; - f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; - f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0; - - q0 = x0*f1 + x1*f0; - q1 = x0*f2 + x1*f1; - q2 = x0*f3 + x1*f2; - q3 = x0*f4 + x1*f3; - q4 = x0*f5 + x1*f4; - } else { // jx == 2 - f6 = (j >= -6) ? TWO_OVER_PI_TAB[j+6] : 0.0; - f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0; - f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; - f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; - f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; - f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; - f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0; - - q0 = x0*f2 + x1*f1 + x2*f0; - q1 = x0*f3 + x1*f2 + x2*f1; - q2 = x0*f4 + x1*f3 + x2*f2; - q3 = x0*f5 + x1*f4 + x2*f3; - q4 = x0*f6 + x1*f5 + x2*f4; - } - - double twoPowQZero = twoPowNormal(qZero); - - int jz = jk; - - /* - * Unrolling of first round. - */ - - z = q4; - fw = (double)(int)(TWO_POW_N24*z); - iq0 = (int)(z-TWO_POW_24*fw); - z = q3+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq1 = (int)(z-TWO_POW_24*fw); - z = q2+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq2 = (int)(z-TWO_POW_24*fw); - z = q1+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq3 = (int)(z-TWO_POW_24*fw); - z = q0+fw; - iq4 = 0; - iq5 = 0; - - z = (z*twoPowQZero) % 8.0; - n = (int)z; - z -= (double)n; - - ih = 0; - if (qZero > 0) { - // Parentheses against code formatter bug. - i = (iq3>>(24-qZero)); - n += i; - iq3 -= i<<(24-qZero); - ih = iq3>>(23-qZero); - } else if (qZero == 0) { - ih = iq3>>23; - } else if (z >= 0.5) { - ih = 2; - } - - if (ih > 0) { - n += 1; - // carry = 1 is common case, - // so using it as initial value. - int carry = 1; - if (iq0 != 0) { - iq0 = 0x1000000 - iq0; - iq1 = 0xFFFFFF - iq1; - iq2 = 0xFFFFFF - iq2; - iq3 = 0xFFFFFF - iq3; - } else if (iq1 != 0) { - iq1 = 0x1000000 - iq1; - iq2 = 0xFFFFFF - iq2; - iq3 = 0xFFFFFF - iq3; - } else if (iq2 != 0) { - iq2 = 0x1000000 - iq2; - iq3 = 0xFFFFFF - iq3; - } else if (iq3 != 0) { - iq3 = 0x1000000 - iq3; - } else { - carry = 0; - } - if (qZero > 0) { - if (qZero == 1) { - iq3 &= 0x7FFFFF; - } else if (qZero == 2) { - iq3 &= 0x3FFFFF; - } - } - if (ih == 2) { - z = 1.0 - z; - if (carry != 0) { - z -= twoPowQZero; - } - } - } - - if (z == 0.0) { - if (iq3 == 0) { - // With random values of random magnitude, - // probability for this to happen seems lower than 1e-6. - // jz would be more than just incremented by one, - // which our unrolling doesn't support. - return jdkRemainderPiO2(angle, negateRem); - } - if (jx == 0) { - f5 = TWO_OVER_PI_TAB[jv+5]; - q5 = x0*f5; - } else if (jx == 1) { - f6 = TWO_OVER_PI_TAB[jv+5]; - q5 = x0*f6 + x1*f5; - } else { // jx == 2 - f7 = TWO_OVER_PI_TAB[jv+5]; - q5 = x0*f7 + x1*f6 + x2*f5; - } - - jz++; - - /* - * Unrolling of second round. - */ - - z = q5; - fw = (double)(int)(TWO_POW_N24*z); - iq0 = (int)(z-TWO_POW_24*fw); - z = q4+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq1 = (int)(z-TWO_POW_24*fw); - z = q3+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq2 = (int)(z-TWO_POW_24*fw); - z = q2+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq3 = (int)(z-TWO_POW_24*fw); - z = q1+fw; - fw = (double)(int)(TWO_POW_N24*z); - iq4 = (int)(z-TWO_POW_24*fw); - z = q0+fw; - iq5 = 0; - - z = (z*twoPowQZero) % 8.0; - n = (int)z; - z -= (double)n; - - ih = 0; - if (qZero > 0) { - // Parentheses against code formatter bug. - i = (iq4>>(24-qZero)); - n += i; - iq4 -= i<<(24-qZero); - ih = iq4>>(23-qZero); - } else if (qZero == 0) { - ih = iq4>>23; - } else if (z >= 0.5) { - ih = 2; - } - - if (ih > 0) { - n += 1; - // carry = 1 is common case, - // so using it as initial value. - int carry = 1; - if (iq0 != 0) { - iq0 = 0x1000000 - iq0; - iq1 = 0xFFFFFF - iq1; - iq2 = 0xFFFFFF - iq2; - iq3 = 0xFFFFFF - iq3; - iq4 = 0xFFFFFF - iq4; - } else if (iq1 != 0) { - iq1 = 0x1000000 - iq1; - iq2 = 0xFFFFFF - iq2; - iq3 = 0xFFFFFF - iq3; - iq4 = 0xFFFFFF - iq4; - } else if (iq2 != 0) { - iq2 = 0x1000000 - iq2; - iq3 = 0xFFFFFF - iq3; - iq4 = 0xFFFFFF - iq4; - } else if (iq3 != 0) { - iq3 = 0x1000000 - iq3; - iq4 = 0xFFFFFF - iq4; - } else if (iq4 != 0) { - iq4 = 0x1000000 - iq4; - } else { - carry = 0; - } - if (qZero > 0) { - if (qZero == 1) { - iq4 &= 0x7FFFFF; - } else if (qZero == 2) { - iq4 &= 0x3FFFFF; - } - } - if (ih == 2) { - z = 1.0 - z; - if (carry != 0) { - z -= twoPowQZero; - } - } - } - - if (z == 0.0) { - if (iq4 == 0) { - // Case not encountered in tests, but still handling it. - // Would require a third loop unrolling. - return jdkRemainderPiO2(angle, negateRem); - } else { - // z == 0.0, and iq4 != 0, - // so we remove 24 from qZero only once, - // but since we no longer use qZero, - // we just bother to multiply its 2-power - // by 2^-24. - jz--; - twoPowQZero *= TWO_POW_N24; - } - } else { - // z != 0.0 at end of second round. - } - } else { - // z != 0.0 at end of first round. - } - - /* - * After loop. - */ - - if (z != 0.0) { - z /= twoPowQZero; - if (z >= TWO_POW_24) { - fw = (double)(int)(TWO_POW_N24*z); - if (jz == jk) { - iq4 = (int)(z-TWO_POW_24*fw); - jz++; // jz to 5 - // Not using qZero anymore so not updating it. - twoPowQZero *= TWO_POW_24; - iq5 = (int)fw; - } else { // jz == jk+1 == 5 - // Case not encountered in tests, but still handling it. - // Would require use of iq6, with jz = 6. - return jdkRemainderPiO2(angle, negateRem); - } - } else { - if (jz == jk) { - iq4 = (int)z; - } else { // jz == jk+1 == 5 - // Case not encountered in tests, but still handling it. - iq5 = (int)z; - } - } - } - - fw = twoPowQZero; - - if (jz == 5) { - q5 = fw*(double)iq5; - fw *= TWO_POW_N24; - } else { - q5 = 0.0; - } - q4 = fw*(double)iq4; - fw *= TWO_POW_N24; - q3 = fw*(double)iq3; - fw *= TWO_POW_N24; - q2 = fw*(double)iq2; - fw *= TWO_POW_N24; - q1 = fw*(double)iq1; - fw *= TWO_POW_N24; - q0 = fw*(double)iq0; - - /* - * We just use HI part of the result. - */ - - fw = PIO2_TAB0*q5; - fw += PIO2_TAB0*q4 + PIO2_TAB1*q5; - fw += PIO2_TAB0*q3 + PIO2_TAB1*q4 + PIO2_TAB2*q5; - fw += PIO2_TAB0*q2 + PIO2_TAB1*q3 + PIO2_TAB2*q4 + PIO2_TAB3*q5; - fw += PIO2_TAB0*q1 + PIO2_TAB1*q2 + PIO2_TAB2*q3 + PIO2_TAB3*q4 + PIO2_TAB4*q5; - fw += PIO2_TAB0*q0 + PIO2_TAB1*q1 + PIO2_TAB2*q2 + PIO2_TAB3*q3 + PIO2_TAB4*q4 + PIO2_TAB5*q5; - - if ((ih != 0) ^ negateRem) { - fw = -fw; - } - - return encodeRemainderAndQuadrant(fw, n&3); - } - - //-------------------------------------------------------------------------- - // PRIVATE METHODS - //-------------------------------------------------------------------------- - - /** - * Redefined here, to avoid cyclic dependency with (Strict)FastMath. - * - * @param value A double value. - * @return -1 if sign bit is 1, 1 if sign bit is 0. - */ - private static long signFromBit_antiCyclic(double value) { - // Returning a long, to avoid useless cast into int. - return ((Double.doubleToRawLongBits(value)>>62)|1); - } - - private static boolean getBooleanProperty( - final String key, - boolean defaultValue) { - final String tmp = System.getProperty(key); - if (tmp != null) { - return Boolean.parseBoolean(tmp); - } else { - return defaultValue; - } - } - - /** - * Use look-up tables size power through this method, - * to make sure is it small in case java.lang.Math - * is directly used. - */ - private static int getTabSizePower(int tabSizePower) { - return (FM_USE_JDK_MATH && SFM_USE_JDK_MATH) ? Math.min(2, tabSizePower) : tabSizePower; - } -} diff --git a/src/main/java/net/jafama/DoubleWrapper.java b/src/main/java/net/jafama/DoubleWrapper.java deleted file mode 100644 index a73de33..0000000 --- a/src/main/java/net/jafama/DoubleWrapper.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 Jeff Hain - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.jafama; - -public class DoubleWrapper { - public double value; - @Override - public String toString() { - return Double.toString(this.value); - } -} diff --git a/src/main/java/net/jafama/FastMath.java b/src/main/java/net/jafama/FastMath.java deleted file mode 100644 index 483d56d..0000000 --- a/src/main/java/net/jafama/FastMath.java +++ /dev/null @@ -1,2975 +0,0 @@ -/* - * Copyright 2012-2015 Jeff Hain - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * ============================================================================= - * Notice of fdlibm package this program is partially derived from: - * - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ============================================================================= - */ -package net.jafama; - -/** - * Class providing math treatments that: - * - are meant to be faster than java.lang.Math class equivalents (if any), - * - are still somehow accurate and robust (handling of NaN and such), - * - do not (or not directly) generate objects at run time (no "new"). - * - * Other than optimized treatments, a valuable feature of this class is the - * presence of angles normalization methods, derived from those used in - * java.lang.Math (for which, sadly, no API is provided, letting everyone - * with the terrible responsibility of writing their own ones). - * - * Non-redefined methods Math methods are also available, for easy replacement, - * even though for some of them, such as for incrementExact, you might want to - * stick to Math versions to benefit from eventual JVM intrinsics. - * - * Use of look-up tables: around 1 Mo total, and initialized lazily or on first - * call to initTables(). - * - * Depending on JVM, or JVM options, these treatments can actually be slower - * than Math ones. - * In particular, they can be slower if not optimized by the JIT, which you - * can see with -Xint JVM option. - * Another cause of slowness can be cache-misses on look-up tables. - * Also, look-up tables initialization typically takes multiple hundreds of - * milliseconds (and is about twice slower in J6 than in J5, and in J7 than in - * J6, possibly due to intrinsifications preventing optimizations such as use - * of hardware sqrt, and Math delegating to StrictMath with JIT optimizations - * not yet up during class load). - * As a result, you might want to make these treatments not use tables, - * and delegate to corresponding Math methods, when they are available in the - * lowest supported Java version, by using the appropriate property (see below). - * - * Methods with same signature than Math ones, are meant to return - * "good" approximations on all range. - * Methods terminating with "Fast" are meant to return "good" approximation - * on a reduced range only. - * Methods terminating with "Quick" are meant to be quick, but do not - * return a good approximation, and might only work on a reduced range. - * - * Properties: - * - * - jafama.usejdk (boolean, default is false): - * If true, for redefined methods, as well as their "Fast" or "Quick" - * terminated counterparts, instead of using redefined computations, - * delegating to Math, when available in required Java version. - * - * - jafama.fastlog (boolean, default is false): - * If true, using redefined computations for log(double) and - * log10(double), else they delegate to Math.log(double) and - * Math.log10(double). - * False by default because Math.log(double) and Math.log10(double) - * seem usually fast (redefined log(double) might be even faster, - * but is less accurate). - * - * - jafama.fastsqrt (boolean, default is false): - * If true, using redefined computation for sqrt(double), - * else it delegates to Math.sqrt(double). - * False by default because Math.sqrt(double) seems usually fast. - */ -public final class FastMath extends CmnFastMath { - - //-------------------------------------------------------------------------- - // CONFIGURATION - //-------------------------------------------------------------------------- - - private static final boolean USE_JDK_MATH = FM_USE_JDK_MATH; - - private static final boolean USE_REDEFINED_LOG = FM_USE_REDEFINED_LOG; - - private static final boolean USE_REDEFINED_SQRT = FM_USE_REDEFINED_SQRT; - - private static final boolean USE_POWTABS_FOR_ASIN = FM_USE_POWTABS_FOR_ASIN; - - //-------------------------------------------------------------------------- - // PUBLIC METHODS - //-------------------------------------------------------------------------- - - /* - * trigonometry - */ - - /** - * @param angle Angle in radians. - * @return Angle sine. - */ - public static double sin(double angle) { - if (USE_JDK_MATH) { - return Math.sin(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { - if (false) { - // Can give very bad relative error near PI (mod 2*PI). - angle = remainderTwoPi(angle); - if (angle < 0.0) { - angle = -angle; - negateResult = !negateResult; - } - } else { - final long remAndQuad = remainderPiO2(angle); - angle = decodeRemainder(remAndQuad); - final double sin; - final int q = decodeQuadrant(remAndQuad); - if (q == 0) { - sin = sin(angle); - } else if (q == 1) { - sin = cos(angle); - } else if (q == 2) { - sin = -sin(angle); - } else { - sin = -cos(angle); - } - return (negateResult ? -sin : sin); - } - } - // index: possibly outside tables range. - int index = (int)(angle * SIN_COS_INDEXER + 0.5); - double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO; - // Making sure index is within tables range. - // Last value of each table is the same than first, - // so we ignore it (tabs size minus one) for modulo. - index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1) - double indexSin = MyTSinCos.sinTab[index]; - double indexCos = MyTSinCos.cosTab[index]; - double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4))); - return negateResult ? -result : result; - } - - /** - * Quick sin, with accuracy of about 1.6e-3 (PI/) - * for |angle| < 6588395.0 (Integer.MAX_VALUE * (2*PI/) - 2) - * (- 2 due to removing PI/2 before using cosine tab), - * and no accuracy at all for larger values. - * - * @param angle Angle in radians. - * @return Angle sine. - */ - public static double sinQuick(double angle) { - if (USE_JDK_MATH) { - return Math.sin(angle); - } - return MyTSinCos.cosTab[((int)(Math.abs(angle-Math.PI/2) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; - } - - /** - * @param angle Angle in radians. - * @return Angle cosine. - */ - public static double cos(double angle) { - if (USE_JDK_MATH) { - return Math.cos(angle); - } - angle = Math.abs(angle); - if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { - if (false) { - // Can give very bad relative error near PI (mod 2*PI). - angle = remainderTwoPi(angle); - if (angle < 0.0) { - angle = -angle; - } - } else { - final long remAndQuad = remainderPiO2(angle); - angle = decodeRemainder(remAndQuad); - final double cos; - final int q = decodeQuadrant(remAndQuad); - if (q == 0) { - cos = cos(angle); - } else if (q == 1) { - cos = -sin(angle); - } else if (q == 2) { - cos = -cos(angle); - } else { - cos = sin(angle); - } - return cos; - } - } - // index: possibly outside tables range. - int index = (int)(angle * SIN_COS_INDEXER + 0.5); - double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO; - // Making sure index is within tables range. - // Last value of each table is the same than first, - // so we ignore it (tabs size minus one) for modulo. - index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1) - double indexCos = MyTSinCos.cosTab[index]; - double indexSin = MyTSinCos.sinTab[index]; - return indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); - } - - /** - * Quick cos, with accuracy of about 1.6e-3 (PI/) - * for |angle| < 6588397.0 (Integer.MAX_VALUE * (2*PI/)), - * and no accuracy at all for larger values. - * - * @param angle Angle in radians. - * @return Angle cosine. - */ - public static double cosQuick(double angle) { - if (USE_JDK_MATH) { - return Math.cos(angle); - } - return MyTSinCos.cosTab[((int)(Math.abs(angle) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; - } - - /** - * Computes sine and cosine together. - * - * @param angle Angle in radians. - * @param cosine (out) Angle cosine. - * @return Angle sine. - */ - public static double sinAndCos(double angle, DoubleWrapper cosine) { - if (USE_JDK_MATH) { - cosine.value = Math.cos(angle); - return Math.sin(angle); - } - // Using the same algorithm than sin(double) method, - // and computing also cosine at the end. - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { - if (false) { - // Can give very bad relative error near PI (mod 2*PI). - angle = remainderTwoPi(angle); - if (angle < 0.0) { - angle = -angle; - negateResult = !negateResult; - } - } else { - final long remAndQuad = remainderPiO2(angle); - angle = decodeRemainder(remAndQuad); - final double sin; - final int q = decodeQuadrant(remAndQuad); - if (q == 0) { - sin = sin(angle); - cosine.value = cos(angle); - } else if (q == 1) { - sin = cos(angle); - cosine.value = -sin(angle); - } else if (q == 2) { - sin = -sin(angle); - cosine.value = -cos(angle); - } else { - sin = -cos(angle); - cosine.value = sin(angle); - } - return (negateResult ? -sin : sin); - } - } - int index = (int)(angle * SIN_COS_INDEXER + 0.5); - double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO; - index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1) - double indexSin = MyTSinCos.sinTab[index]; - double indexCos = MyTSinCos.cosTab[index]; - // Could factor some multiplications (delta * factorials), but then is less accurate. - cosine.value = indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); - double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4))); - return negateResult ? -result : result; - } - - /** - * Can have very bad relative error near +-PI/2, - * but of the same magnitude than the relative delta between - * StrictMath.tan(PI/2) and StrictMath.tan(nextDown(PI/2)). - * - * @param angle Angle in radians. - * @return Angle tangent. - */ - public static double tan(double angle) { - if (USE_JDK_MATH) { - return Math.tan(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle > TAN_MAX_VALUE_FOR_INT_MODULO) { - angle = remainderPi(angle); - if (angle < 0.0) { - angle = -angle; - negateResult = !negateResult; - } - } - // index: possibly outside tables range. - int index = (int)(angle * TAN_INDEXER + 0.5); - double delta = (angle - index * TAN_DELTA_HI) - index * TAN_DELTA_LO; - // Making sure index is within tables range. - // index modulo PI, i.e. 2*(virtual tab size minus one). - index &= (2*(TAN_VIRTUAL_TABS_SIZE-1)-1); // index % (2*(TAN_VIRTUAL_TABS_SIZE-1)) - // Here, index is in [0,2*(TAN_VIRTUAL_TABS_SIZE-1)-1], i.e. indicates an angle in [0,PI[. - if (index > (TAN_VIRTUAL_TABS_SIZE-1)) { - index = (2*(TAN_VIRTUAL_TABS_SIZE-1)) - index; - delta = -delta; - negateResult = !negateResult; - } - double result; - if (index < TAN_TABS_SIZE) { - result = MyTTan.tanTab[index] - + delta * (MyTTan.tanDer1DivF1Tab[index] - + delta * (MyTTan.tanDer2DivF2Tab[index] - + delta * (MyTTan.tanDer3DivF3Tab[index] - + delta * MyTTan.tanDer4DivF4Tab[index]))); - } else { // angle in ]TAN_MAX_VALUE_FOR_TABS,TAN_MAX_VALUE_FOR_INT_MODULO], or angle is NaN - // Using tan(angle) == 1/tan(PI/2-angle) formula: changing angle (index and delta), and inverting. - index = (TAN_VIRTUAL_TABS_SIZE-1) - index; - result = 1/(MyTTan.tanTab[index] - - delta * (MyTTan.tanDer1DivF1Tab[index] - - delta * (MyTTan.tanDer2DivF2Tab[index] - - delta * (MyTTan.tanDer3DivF3Tab[index] - - delta * MyTTan.tanDer4DivF4Tab[index])))); - } - return negateResult ? -result : result; - } - - /** - * @param value Value in [-1,1]. - * @return Value arcsine, in radians, in [-PI/2,PI/2]. - */ - public static double asin(double value) { - if (USE_JDK_MATH) { - return Math.asin(value); - } - boolean negateResult = false; - if (value < 0.0) { - value = -value; - negateResult = true; - } - if (value <= ASIN_MAX_VALUE_FOR_TABS) { - int index = (int)(value * ASIN_INDEXER + 0.5); - double delta = value - index * ASIN_DELTA; - double result = MyTAsin.asinTab[index] - + delta * (MyTAsin.asinDer1DivF1Tab[index] - + delta * (MyTAsin.asinDer2DivF2Tab[index] - + delta * (MyTAsin.asinDer3DivF3Tab[index] - + delta * MyTAsin.asinDer4DivF4Tab[index]))); - return negateResult ? -result : result; - } else if (USE_POWTABS_FOR_ASIN && (value <= ASIN_MAX_VALUE_FOR_POWTABS)) { - int index = (int)(powFast(value * ASIN_POWTABS_ONE_DIV_MAX_VALUE, ASIN_POWTABS_POWER) * ASIN_POWTABS_SIZE_MINUS_ONE + 0.5); - double delta = value - MyTAsinPow.asinParamPowTab[index]; - double result = MyTAsinPow.asinPowTab[index] - + delta * (MyTAsinPow.asinDer1DivF1PowTab[index] - + delta * (MyTAsinPow.asinDer2DivF2PowTab[index] - + delta * (MyTAsinPow.asinDer3DivF3PowTab[index] - + delta * MyTAsinPow.asinDer4DivF4PowTab[index]))); - return negateResult ? -result : result; - } else { // value > ASIN_MAX_VALUE_FOR_TABS, or value is NaN - // This part is derived from fdlibm. - if (value < 1.0) { - double t = (1.0 - value)*0.5; - double p = t*(ASIN_PS0+t*(ASIN_PS1+t*(ASIN_PS2+t*(ASIN_PS3+t*(ASIN_PS4+t*ASIN_PS5))))); - double q = 1.0+t*(ASIN_QS1+t*(ASIN_QS2+t*(ASIN_QS3+t*ASIN_QS4))); - double s = sqrt(t); - double z = s+s*(p/q); - double result = ASIN_PIO2_HI-((z+z)-ASIN_PIO2_LO); - return negateResult ? -result : result; - } else { // value >= 1.0, or value is NaN - if (value == 1.0) { - return negateResult ? -Math.PI/2 : Math.PI/2; - } else { - return Double.NaN; - } - } - } - } - - /** - * If value is not NaN and is outside [-1,1] range, closest value in this range is used. - * - * @param value Value in [-1,1]. - * @return Value arcsine, in radians, in [-PI/2,PI/2]. - */ - public static double asinInRange(double value) { - if (value <= -1.0) { - return -Math.PI/2; - } else if (value >= 1.0) { - return Math.PI/2; - } else { - return asin(value); - } - } - - /** - * @param value Value in [-1,1]. - * @return Value arccosine, in radians, in [0,PI]. - */ - public static double acos(double value) { - if (USE_JDK_MATH) { - return Math.acos(value); - } - return Math.PI/2 - asin(value); - } - - /** - * If value is not NaN and is outside [-1,1] range, - * closest value in this range is used. - * - * @param value Value in [-1,1]. - * @return Value arccosine, in radians, in [0,PI]. - */ - public static double acosInRange(double value) { - if (value <= -1.0) { - return Math.PI; - } else if (value >= 1.0) { - return 0.0; - } else { - return acos(value); - } - } - - /** - * @param value A double value. - * @return Value arctangent, in radians, in [-PI/2,PI/2]. - */ - public static double atan(double value) { - if (USE_JDK_MATH) { - return Math.atan(value); - } - boolean negateResult = false; - if (value < 0.0) { - value = -value; - negateResult = true; - } - if (value == 1.0) { - // We want "exact" result for 1.0. - return negateResult ? -Math.PI/4 : Math.PI/4; - } else if (value <= ATAN_MAX_VALUE_FOR_TABS) { - int index = (int)(value * ATAN_INDEXER + 0.5); - double delta = value - index * ATAN_DELTA; - double result = MyTAtan.atanTab[index] - + delta * (MyTAtan.atanDer1DivF1Tab[index] - + delta * (MyTAtan.atanDer2DivF2Tab[index] - + delta * (MyTAtan.atanDer3DivF3Tab[index] - + delta * MyTAtan.atanDer4DivF4Tab[index]))); - return negateResult ? -result : result; - } else { // value > ATAN_MAX_VALUE_FOR_TABS, or value is NaN - // This part is derived from fdlibm. - if (value < TWO_POW_66) { - double x = -1/value; - double x2 = x*x; - double x4 = x2*x2; - double s1 = x2*(ATAN_AT0+x4*(ATAN_AT2+x4*(ATAN_AT4+x4*(ATAN_AT6+x4*(ATAN_AT8+x4*ATAN_AT10))))); - double s2 = x4*(ATAN_AT1+x4*(ATAN_AT3+x4*(ATAN_AT5+x4*(ATAN_AT7+x4*ATAN_AT9)))); - double result = ATAN_HI3-((x*(s1+s2)-ATAN_LO3)-x); - return negateResult ? -result : result; - } else { // value >= 2^66, or value is NaN - if (value != value) { - return Double.NaN; - } else { - return negateResult ? -Math.PI/2 : Math.PI/2; - } - } - } - } - - /** - * For special values for which multiple conventions could be adopted, - * behaves like Math.atan2(double,double). - * - * @param y Coordinate on y axis. - * @param x Coordinate on x axis. - * @return Angle from x axis positive side to (x,y) position, in radians, in [-PI,PI]. - * Angle measure is positive when going from x axis to y axis (positive sides). - */ - public static double atan2(double y, double x) { - if (USE_JDK_MATH) { - return Math.atan2(y,x); - } - /* - * Using sub-methods, to make method lighter for general case, - * and to avoid JIT-optimization crash on NaN. - */ - if (x > 0.0) { - if (y == 0.0) { - // +-0.0 - return y; - } - if (x == Double.POSITIVE_INFINITY) { - return atan2_pinf_yyy(y); - } else { - return atan(y/x); - } - } else if (x < 0.0) { - if (y == 0.0) { - return signFromBit(y) * Math.PI; - } - if (x == Double.NEGATIVE_INFINITY) { - return atan2_ninf_yyy(y); - } else if (y > 0.0) { - return Math.PI/2 - atan(x/y); - } else if (y < 0.0) { - return -Math.PI/2 - atan(x/y); - } else { - return Double.NaN; - } - } else { - return atan2_yyy_zeroOrNaN(y, x); - } - } - - /** - * Gives same result as Math.toRadians for some particular values - * like 90.0, 180.0 or 360.0, but is faster (no division). - * - * @param angdeg Angle value in degrees. - * @return Angle value in radians. - */ - public static double toRadians(double angdeg) { - if (USE_JDK_MATH) { - return Math.toRadians(angdeg); - } - return angdeg * (Math.PI/180); - } - - /** - * Gives same result as Math.toDegrees for some particular values - * like Math.PI/2, Math.PI or 2*Math.PI, but is faster (no division). - * - * @param angrad Angle value in radians. - * @return Angle value in degrees. - */ - public static double toDegrees(double angrad) { - if (USE_JDK_MATH) { - return Math.toDegrees(angrad); - } - return angrad * (180/Math.PI); - } - - /** - * @param sign Sign of the angle: true for positive, false for negative. - * @param degrees Degrees, in [0,180]. - * @param minutes Minutes, in [0,59]. - * @param seconds Seconds, in [0.0,60.0[. - * @return Angle in radians. - */ - public static double toRadians(boolean sign, int degrees, int minutes, double seconds) { - return toRadians(toDegrees(sign, degrees, minutes, seconds)); - } - - /** - * @param sign Sign of the angle: true for positive, false for negative. - * @param degrees Degrees, in [0,180]. - * @param minutes Minutes, in [0,59]. - * @param seconds Seconds, in [0.0,60.0[. - * @return Angle in degrees. - */ - public static double toDegrees(boolean sign, int degrees, int minutes, double seconds) { - double signFactor = sign ? 1.0 : -1.0; - return signFactor * (degrees + (1.0/60)*(minutes + (1.0/60)*seconds)); - } - - /** - * @param angrad Angle in radians. - * @param degrees (out) Degrees, in [0,180]. - * @param minutes (out) Minutes, in [0,59]. - * @param seconds (out) Seconds, in [0.0,60.0[. - * @return true if the resulting angle in [-180deg,180deg] is positive, false if it is negative. - */ - public static boolean toDMS(double angrad, IntWrapper degrees, IntWrapper minutes, DoubleWrapper seconds) { - // Computing longitude DMS. - double tmp = toDegrees(normalizeMinusPiPi(angrad)); - boolean isNeg = (tmp < 0.0); - if (isNeg) { - tmp = -tmp; - } - degrees.value = (int)tmp; - tmp = (tmp-degrees.value)*60.0; - minutes.value = (int)tmp; - seconds.value = Math.min((tmp-minutes.value)*60.0,DOUBLE_BEFORE_60); - return !isNeg; - } - - /** - * NB: Since 2*Math.PI < 2*PI, a span of 2*Math.PI does not mean full angular range. - * ex.: isInClockwiseDomain(0.0, 2*Math.PI, -1e-20) returns false. - * ---> For full angular range, use a span > 2*Math.PI, like 2*PI_SUP constant of this class. - * - * @param startAngRad An angle, in radians. - * @param angSpanRad An angular span, >= 0.0, in radians. - * @param angRad An angle, in radians. - * @return true if angRad is in the clockwise angular domain going from startAngRad, over angSpanRad, - * extremities included, false otherwise. - */ - public static boolean isInClockwiseDomain(double startAngRad, double angSpanRad, double angRad) { - if (Math.abs(angRad) < -TWO_MATH_PI_IN_MINUS_PI_PI) { - // special case for angular values of small magnitude - if (angSpanRad <= 2*Math.PI) { - if (angSpanRad < 0.0) { - // empty domain - return false; - } - // angSpanRad is in [0,2*PI] - startAngRad = normalizeMinusPiPi(startAngRad); - double endAngRad = normalizeMinusPiPi(startAngRad + angSpanRad); - if (startAngRad <= endAngRad) { - return (angRad >= startAngRad) && (angRad <= endAngRad); - } else { - return (angRad >= startAngRad) || (angRad <= endAngRad); - } - } else { // angSpanRad > 2*Math.PI, or is NaN - return (angSpanRad == angSpanRad); - } - } else { - // general case - return (normalizeZeroTwoPi(angRad - startAngRad) <= angSpanRad); - } - } - - /* - * hyperbolic trigonometry - */ - - /** - * Some properties of sinh(x) = (exp(x)-exp(-x))/2: - * 1) defined on ]-Infinity,+Infinity[ - * 2) result in ]-Infinity,+Infinity[ - * 3) sinh(x) = -sinh(-x) (implies sinh(0) = 0) - * 4) sinh(epsilon) ~= epsilon - * 5) lim(sinh(x),x->+Infinity) = +Infinity - * (y increasing exponentially faster than x) - * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, - * i.e. a bit further than exp(x) - * - * @param value A double value. - * @return Value hyperbolic sine. - */ - public static double sinh(double value) { - if (USE_JDK_MATH) { - return Math.sinh(value); - } - // sinh(x) = (exp(x)-exp(-x))/2 - double h; - if (value < 0.0) { - value = -value; - h = -0.5; - } else { - h = 0.5; - } - if (value < 22.0) { - if (value < TWO_POW_N28) { - return (h < 0.0) ? -value : value; - } else { - // sinh(x) - // = (exp(x)-exp(-x))/2 - // = (exp(x)-1/exp(x))/2 - // = (expm1(x) + 1 - 1/(expm1(x)+1))/2 - // = (expm1(x) + (expm1(x)+1)/(expm1(x)+1) - 1/(expm1(x)+1))/2 - // = (expm1(x) + expm1(x)/(expm1(x)+1))/2 - double t = expm1(value); - // Might be more accurate, if value < 1: return h*((t+t)-t*t/(t+1.0)). - return h * (t + t/(t+1.0)); - } - } else if (value < LOG_DOUBLE_MAX_VALUE) { - return h * exp(value); - } else { - double t = exp(value*0.5); - return (h*t)*t; - } - } - - /** - * Some properties of cosh(x) = (exp(x)+exp(-x))/2: - * 1) defined on ]-Infinity,+Infinity[ - * 2) result in [1,+Infinity[ - * 3) cosh(0) = 1 - * 4) cosh(x) = cosh(-x) - * 5) lim(cosh(x),x->+Infinity) = +Infinity - * (y increasing exponentially faster than x) - * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, - * i.e. a bit further than exp(x) - * - * @param value A double value. - * @return Value hyperbolic cosine. - */ - public static double cosh(double value) { - if (USE_JDK_MATH) { - return Math.cosh(value); - } - // cosh(x) = (exp(x)+exp(-x))/2 - if (value < 0.0) { - value = -value; - } - if (value < LOG_TWO_POW_27) { - if (value < TWO_POW_N27) { - // cosh(x) - // = (exp(x)+exp(-x))/2 - // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 - // = 1+x^2/2!+x^4/4!+... - // For value of x small in magnitude, the sum of the terms does not add to 1. - return 1; - } else { - // cosh(x) - // = (exp(x)+exp(-x))/2 - // = (exp(x)+1/exp(x))/2 - double t = exp(value); - return 0.5 * (t+1/t); - } - } else if (value < LOG_DOUBLE_MAX_VALUE) { - return 0.5 * exp(value); - } else { - double t = exp(value*0.5); - return (0.5*t)*t; - } - } - - /** - * Much more accurate than cosh(value)-1, - * for arguments (and results) close to zero. - * - * coshm1(-0.0) = -0.0, for homogeneity with - * acosh1p(-0.0) = -0.0. - * - * @param value A double value. - * @return Value hyperbolic cosine, minus 1. - */ - public static double coshm1(double value) { - // cosh(x)-1 = (exp(x)+exp(-x))/2 - 1 - if (value < 0.0) { - value = -value; - } - if (value < LOG_TWO_POW_27) { - if (value < TWO_POW_N27) { - if (value == 0.0) { - // +-0.0 - return value; - } - // Using (expm1(x)+expm1(-x))/2 - // is not accurate for tiny values, - // for expm1 results are of higher - // magnitude than the result and - // of different signs, such as their - // sum is not accurate. - // cosh(x) - 1 - // = (exp(x)+exp(-x))/2 - 1 - // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 - 1 - // = x^2/2!+x^4/4!+... - // ~= x^2 * (1/2 + x^2 * 1/24) - // = x^2 * 0.5 (since x < 2^-27) - return 0.5 * value*value; - } else { - // cosh(x) - 1 - // = (exp(x)+exp(-x))/2 - 1 - // = (exp(x)-1+exp(-x)-1)/2 - // = (expm1(x)+expm1(-x))/2 - return 0.5 * (expm1(value)+expm1(-value)); - } - } else if (value < LOG_DOUBLE_MAX_VALUE) { - return 0.5 * exp(value) - 1.0; - } else { - // No need to subtract 1 from result. - double t = exp(value*0.5); - return (0.5*t)*t; - } - } - - /** - * Computes hyperbolic sine and hyperbolic cosine together. - * - * @param value A double value. - * @param hcosine (out) Value hyperbolic cosine. - * @return Value hyperbolic sine. - */ - public static double sinhAndCosh(double value, DoubleWrapper hcosine) { - if (USE_JDK_MATH) { - hcosine.value = Math.cosh(value); - return Math.sinh(value); - } - // Mixup of sinh and cosh treatments: if you modify them, - // you might want to also modify this. - double h; - if (value < 0.0) { - value = -value; - h = -0.5; - } else { - h = 0.5; - } - final double hsine; - // LOG_TWO_POW_27 = 18.714973875118524 - if (value < LOG_TWO_POW_27) { // test from cosh - // sinh - if (value < TWO_POW_N28) { - hsine = (h < 0.0) ? -value : value; - } else { - double t = expm1(value); - hsine = h * (t + t/(t+1.0)); - } - // cosh - if (value < TWO_POW_N27) { - hcosine.value = 1; - } else { - double t = exp(value); - hcosine.value = 0.5 * (t+1/t); - } - } else if (value < 22.0) { // test from sinh - // Here, value is in [18.714973875118524,22.0[. - double t = expm1(value); - hsine = h * (t + t/(t+1.0)); - hcosine.value = 0.5 * (t+1.0); - } else { - if (value < LOG_DOUBLE_MAX_VALUE) { - hsine = h * exp(value); - } else { - double t = exp(value*0.5); - hsine = (h*t)*t; - } - hcosine.value = Math.abs(hsine); - } - return hsine; - } - - /** - * Some properties of tanh(x) = sinh(x)/cosh(x) = (exp(2*x)-1)/(exp(2*x)+1): - * 1) defined on ]-Infinity,+Infinity[ - * 2) result in ]-1,1[ - * 3) tanh(x) = -tanh(-x) (implies tanh(0) = 0) - * 4) tanh(epsilon) ~= epsilon - * 5) lim(tanh(x),x->+Infinity) = 1 - * 6) reaches 1 (double loss of precision) for x = 19.061547465398498 - * - * @param value A double value. - * @return Value hyperbolic tangent. - */ - public static double tanh(double value) { - if (USE_JDK_MATH) { - return Math.tanh(value); - } - // tanh(x) = sinh(x)/cosh(x) - // = (exp(x)-exp(-x))/(exp(x)+exp(-x)) - // = (exp(2*x)-1)/(exp(2*x)+1) - boolean negateResult = false; - if (value < 0.0) { - value = -value; - negateResult = true; - } - double z; - if (value < TANH_1_THRESHOLD) { - if (value < TWO_POW_N55) { - return negateResult ? -value*(1.0-value) : value*(1.0+value); - } else if (value >= 1) { - z = 1.0-2.0/(expm1(value+value)+2.0); - } else { - double t = expm1(-(value+value)); - z = -t/(t+2.0); - } - } else { - z = (value != value) ? Double.NaN : 1.0; - } - return negateResult ? -z : z; - } - - /** - * Some properties of asinh(x) = log(x + sqrt(x^2 + 1)) - * 1) defined on ]-Infinity,+Infinity[ - * 2) result in ]-Infinity,+Infinity[ - * 3) asinh(x) = -asinh(-x) (implies asinh(0) = 0) - * 4) asinh(epsilon) ~= epsilon - * 5) lim(asinh(x),x->+Infinity) = +Infinity - * (y increasing logarithmically slower than x) - * - * @param value A double value. - * @return Value hyperbolic arcsine. - */ - public static double asinh(double value) { - // asinh(x) = log(x + sqrt(x^2 + 1)) - boolean negateResult = false; - if (value < 0.0) { - value = -value; - negateResult = true; - } - double result; - // (about) smallest possible for - // non-log1p case to be accurate. - if (value < ASINH_LOG1P_THRESHOLD) { - // Around this range, FDLIBM uses - // log1p(value+value*value/(1+sqrt(value*value+1))), - // but it's slower, so we don't use it. - /* - * If x is close to zero, log argument is close to 1, - * so to avoid precision loss we use log1p(double), - * with - * (1+x)^p = 1 + p * x + (p*(p-1))/2! * x^2 + (p*(p-1)*(p-2))/3! * x^3 + ... - * (1+x)^p = 1 + p * x * (1 + (p-1)/2 * x * (1 + (p-2)/3 * x + ...) - * (1+x)^0.5 = 1 + 0.5 * x * (1 + (0.5-1)/2 * x * (1 + (0.5-2)/3 * x + ...) - * (1+x^2)^0.5 = 1 + 0.5 * x^2 * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...) - * x + (1+x^2)^0.5 = 1 + x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...)) - * so - * asinh(x) = log1p(x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...))) - */ - final double x = value; - final double x2 = x*x; - // Enough terms for good accuracy, - // given our threshold. - final double argLog1p = (x * - (1 + 0.5 * x - * (1 + (0.5-1)/2 * x2 - * (1 + (0.5-2)/3 * x2 - * (1 + (0.5-3)/4 * x2 - * (1 + (0.5-4)/5 * x2 - )))))); - result = log1p(argLog1p); - } else if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { - // Around this range, FDLIBM uses - // log(2*value+1/(value+sqrt(value*value+1))), - // but it involves an additional division - // so we don't use it. - result = log(value + sqrt(value*value + 1.0)); - } else { - // log(2*value) would overflow for value > Double.MAX_VALUE/2, - // so we compute otherwise. - result = LOG_2 + log(value); - } - return negateResult ? -result : result; - } - - /** - * Some properties of acosh(x) = log(x + sqrt(x^2 - 1)): - * 1) defined on [1,+Infinity[ - * 2) result in ]0,+Infinity[ (by convention, since cosh(x) = cosh(-x)) - * 3) acosh(1) = 0 - * 4) acosh(1+epsilon) ~= log(1 + sqrt(2*epsilon)) ~= sqrt(2*epsilon) - * 5) lim(acosh(x),x->+Infinity) = +Infinity - * (y increasing logarithmically slower than x) - * - * @param value A double value. - * @return Value hyperbolic arccosine. - */ - public static double acosh(double value) { - if (!(value > 1.0)) { - // NaN, or value <= 1 - if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { - return (value < 1.0) ? Double.NaN : value - 1.0; - } else { - return (value == 1.0) ? 0.0 : Double.NaN; - } - } - double result; - if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { - // Around this range, FDLIBM uses - // log(2*value-1/(value+sqrt(value*value-1))), - // but it involves an additional division - // so we don't use it. - result = log(value + sqrt(value*value - 1.0)); - } else { - // log(2*value) would overflow for value > Double.MAX_VALUE/2, - // so we compute otherwise. - result = LOG_2 + log(value); - } - return result; - } - - /** - * Much more accurate than acosh(1+value), - * for arguments (and results) close to zero. - * - * acosh1p(-0.0) = -0.0, for homogeneity with - * sqrt(-0.0) = -0.0, which looks about the same - * near 0. - * - * @param value A double value. - * @return Hyperbolic arccosine of (1+value). - */ - public static double acosh1p(double value) { - if (!(value > 0.0)) { - // NaN, or value <= 0. - // If value is -0.0, returning -0.0. - if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { - return (value < 0.0) ? Double.NaN : value; - } else { - return (value == 0.0) ? value : Double.NaN; - } - } - double result; - if (value < (ASINH_ACOSH_SQRT_ELISION_THRESHOLD-1)) { - // acosh(1+x) - // = log((1+x) + sqrt((1+x)^2 - 1)) - // = log(1 + x + sqrt(1 + 2*x + x^2 - 1)) - // = log1p(x + sqrt(2*x + x^2)) - // = log1p(x + sqrt(x * (2 + x)) - result = log1p(value + sqrt(value * (2 + value))); - } else { - result = LOG_2 + log(1+value); - } - return result; - } - - /** - * Some properties of atanh(x) = log((1+x)/(1-x))/2: - * 1) defined on ]-1,1[ - * 2) result in ]-Infinity,+Infinity[ - * 3) atanh(-1) = -Infinity (by continuity) - * 4) atanh(1) = +Infinity (by continuity) - * 5) atanh(epsilon) ~= epsilon - * 6) lim(atanh(x),x->1) = +Infinity - * - * @param value A double value. - * @return Value hyperbolic arctangent. - */ - public static double atanh(double value) { - boolean negateResult = false; - if (value < 0.0) { - value = -value; - negateResult = true; - } - double result; - if (!(value < 1.0)) { - // NaN, or value >= 1 - if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { - result = (value > 1.0) ? Double.NaN : Double.POSITIVE_INFINITY + value; - } else { - result = (value == 1.0) ? Double.POSITIVE_INFINITY : Double.NaN; - } - } else { - // For value < 0.5, FDLIBM uses - // 0.5 * log1p((value+value) + (value+value)*value/(1-value)), - // instead, but this is good enough for us. - // atanh(x) - // = log((1+x)/(1-x))/2 - // = log((1-x+2x)/(1-x))/2 - // = log1p(2x/(1-x))/2 - result = 0.5 * log1p((value+value)/(1.0-value)); - } - return negateResult ? -result : result; - } - - /* - * exponentials - */ - - /** - * @param value A double value. - * @return e^value. - */ - public static double exp(double value) { - if (USE_JDK_MATH) { - return Math.exp(value); - } - // exp(x) = exp([x])*exp(y) - // with [x] the integer part of x, and y = x-[x] - // ===> - // We find an approximation of y, called z. - // ===> - // exp(x) = exp([x])*(exp(z)*exp(epsilon)) - // with epsilon = y - z - // ===> - // We have exp([x]) and exp(z) pre-computed in tables, we "just" have to compute exp(epsilon). - // - // We use the same indexing (cast to int) to compute x integer part and the - // table index corresponding to z, to avoid two int casts. - // Also, to optimize index multiplication and division, we use powers of two, - // so that we can do it with bits shifts. - - if (value > EXP_OVERFLOW_LIMIT) { - return Double.POSITIVE_INFINITY; - } else if (!(value >= EXP_UNDERFLOW_LIMIT)) { - return (value != value) ? Double.NaN : 0.0; - } - - final int indexes = (int)(value*EXP_LO_INDEXING); - - final int valueInt; - if (indexes >= 0) { - valueInt = (indexes>>EXP_LO_INDEXING_DIV_SHIFT); - } else { - valueInt = -((-indexes)>>EXP_LO_INDEXING_DIV_SHIFT); - } - final double hiTerm = MyTExp.expHiTab[valueInt-(int)EXP_UNDERFLOW_LIMIT]; - - final int zIndex = indexes - (valueInt< 0.0) { - if (value == Double.POSITIVE_INFINITY) { - return Double.POSITIVE_INFINITY; - } - - // For normal values not close to 1.0, we use the following formula: - // log(value) - // = log(2^exponent*1.mantissa) - // = log(2^exponent) + log(1.mantissa) - // = exponent * log(2) + log(1.mantissa) - // = exponent * log(2) + log(1.mantissaApprox) + log(1.mantissa/1.mantissaApprox) - // = exponent * log(2) + log(1.mantissaApprox) + log(1+epsilon) - // = exponent * log(2) + log(1.mantissaApprox) + epsilon-epsilon^2/2+epsilon^3/3-epsilon^4/4+... - // with: - // 1.mantissaApprox <= 1.mantissa, - // log(1.mantissaApprox) in table, - // epsilon = (1.mantissa/1.mantissaApprox)-1 - // - // To avoid bad relative error for small results, - // values close to 1.0 are treated aside, with the formula: - // log(x) = z*(2+z^2*((2.0/3)+z^2*((2.0/5))+z^2*((2.0/7))+...))) - // with z=(x-1)/(x+1) - - double h; - if (value > 0.95) { - if (value < 1.14) { - double z = (value-1.0)/(value+1.0); - double z2 = z*z; - return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); - } - h = 0.0; - } else if (value < DOUBLE_MIN_NORMAL) { - // Ensuring value is normal. - value *= TWO_POW_52; - // log(x*2^52) - // = log(x)-ln(2^52) - // = log(x)-52*ln(2) - h = -52*LOG_2; - } else { - h = 0.0; - } - - int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); - int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; - // Getting the first LOG_BITS bits of the mantissa. - int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); - - // 1.mantissa/1.mantissaApprox - 1 - double z = (value * twoPowNormalOrSubnormal(-valueExp)) * MyTLog.logXInvTab[xIndex] - 1; - - z *= (1-z*((1.0/2)-z*((1.0/3)))); - - return h + valueExp * LOG_2 + (MyTLog.logXLogTab[xIndex] + z); - - } else if (value == 0.0) { - return Double.NEGATIVE_INFINITY; - } else { // value < 0.0, or value is NaN - return Double.NaN; - } - } - - /** - * Quick log, with a max relative error of about 1.9e-3 - * for values in ]Double.MIN_NORMAL,+Infinity[, and - * worse accuracy outside this range. - * - * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). - * @return Value logarithm (base e). - */ - public static double logQuick(double value) { - if (USE_JDK_MATH) { - return Math.log(value); - } - /* - * Inverse of Schraudolph's method for exp, is very inaccurate near 1, - * and not that fast (even using floats), especially with added if's - * to deal with values near 1, so we don't use it, and use a simplified - * version of our log's redefined algorithm. - */ - - // Simplified version of log's redefined algorithm: - // log(value) ~= exponent * log(2) + log(1.mantissaApprox) - - double h; - if (value > 0.87) { - if (value < 1.16) { - return 2.0 * (value-1.0)/(value+1.0); - } - h = 0.0; - } else if (value < DOUBLE_MIN_NORMAL) { - value *= TWO_POW_52; - h = -52*LOG_2; - } else { - h = 0.0; - } - - int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); - int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; - int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); - - return h + valueExp * LOG_2 + MyTLog.logXLogTab[xIndex]; - } - - /** - * @param value A double value. - * @return Value logarithm (base 10). - */ - public static double log10(double value) { - if (USE_JDK_MATH || (!USE_REDEFINED_LOG)) { - return Math.log10(value); - } - // INV_LOG_10 is < 1, but there is no risk of log(double) - // overflow (positive or negative) while the end result shouldn't, - // since log(Double.MIN_VALUE) and log(Double.MAX_VALUE) have - // magnitudes of just a few hundreds. - return log(value) * INV_LOG_10; - } - - /** - * Much more accurate than log(1+value), - * for arguments (and results) close to zero. - * - * @param value A double value. - * @return Logarithm (base e) of (1+value). - */ - public static double log1p(double value) { - if (USE_JDK_MATH) { - return Math.log1p(value); - } - if (false) { - // This also works. Simpler but a bit slower. - if (value == Double.POSITIVE_INFINITY) { - return Double.POSITIVE_INFINITY; - } - double valuePlusOne = 1+value; - if (valuePlusOne == 1.0) { - return value; - } else { - return log(valuePlusOne)*(value/(valuePlusOne-1.0)); - } - } - if (value > -1.0) { - if (value == Double.POSITIVE_INFINITY) { - return Double.POSITIVE_INFINITY; - } - - // ln'(x) = 1/x - // so - // log(x+epsilon) ~= log(x) + epsilon/x - // - // Let u be 1+value rounded: - // 1+value = u+epsilon - // - // log(1+value) - // = log(u+epsilon) - // ~= log(u) + epsilon/value - // We compute log(u) as done in log(double), and then add the corrective term. - - double valuePlusOne = 1.0+value; - if (valuePlusOne == 1.0) { - return value; - } else if (Math.abs(value) < 0.15) { - double z = value/(value+2.0); - double z2 = z*z; - return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); - } - - int valuePlusOneBitsHi = (int)(Double.doubleToRawLongBits(valuePlusOne)>>32) & 0x7FFFFFFF; - int valuePlusOneExp = (valuePlusOneBitsHi>>20)-MAX_DOUBLE_EXPONENT; - // Getting the first LOG_BITS bits of the mantissa. - int xIndex = ((valuePlusOneBitsHi<<12)>>>(32-LOG_BITS)); - - // 1.mantissa/1.mantissaApprox - 1 - double z = (valuePlusOne * twoPowNormalOrSubnormal(-valuePlusOneExp)) * MyTLog.logXInvTab[xIndex] - 1; - - z *= (1-z*((1.0/2)-z*(1.0/3))); - - // Adding epsilon/valuePlusOne to z, - // with - // epsilon = value - (valuePlusOne-1) - // (valuePlusOne + epsilon ~= 1+value (not rounded)) - - return valuePlusOneExp * LOG_2 + MyTLog.logXLogTab[xIndex] + (z + (value - (valuePlusOne-1))/valuePlusOne); - } else if (value == -1.0) { - return Double.NEGATIVE_INFINITY; - } else { // value < -1.0, or value is NaN - return Double.NaN; - } - } - - /* - * powers - */ - - /** - * 1e-13ish accuracy or better on whole double range. - * - * @param value A double value. - * @param power A power. - * @return value^power. - */ - public static double pow(double value, double power) { - if (USE_JDK_MATH) { - return Math.pow(value,power); - } - if (power == 0.0) { - return 1.0; - } else if (power == 1.0) { - return value; - } - if (value <= 0.0) { - // powerInfo: 0 if not integer, 1 if even integer, -1 if odd integer - int powerInfo; - if (Math.abs(power) >= (TWO_POW_52*2)) { - // The binary digit just before comma is outside mantissa, - // thus it is always 0: power is an even integer. - powerInfo = 1; - } else { - // If power's magnitude permits, we cast into int instead of into long, - // as it is faster. - if (Math.abs(power) <= (double)Integer.MAX_VALUE) { - int powerAsInt = (int)power; - if (power == (double)powerAsInt) { - powerInfo = ((powerAsInt & 1) == 0) ? 1 : -1; - } else { // power is not an integer (and not NaN, due to test against Integer.MAX_VALUE) - powerInfo = 0; - } - } else { - long powerAsLong = (long)power; - if (power == (double)powerAsLong) { - powerInfo = ((powerAsLong & 1) == 0) ? 1 : -1; - } else { // power is not an integer, or is NaN - if (power != power) { - return Double.NaN; - } - powerInfo = 0; - } - } - } - - if (value == 0.0) { - if (power < 0.0) { - return (powerInfo < 0) ? 1/value : Double.POSITIVE_INFINITY; - } else { // power > 0.0 (0 and NaN cases already treated) - return (powerInfo < 0) ? value : 0.0; - } - } else { // value < 0.0 - if (value == Double.NEGATIVE_INFINITY) { - if (powerInfo < 0) { // power odd integer - return (power < 0.0) ? -0.0 : Double.NEGATIVE_INFINITY; - } else { // power even integer, or not an integer - return (power < 0.0) ? 0.0 : Double.POSITIVE_INFINITY; - } - } else { - return (powerInfo == 0) ? Double.NaN : powerInfo * exp(power*log(-value)); - } - } - } else { // value > 0.0, or value is NaN - return exp(power*log(value)); - } - } - - /** - * Quick pow, with a max relative error of about 1e-2 - * for value >= Double.MIN_NORMAL and 1e-10 < |value^power| < 1e10, - * of about 6e-2 for value >= Double.MIN_NORMAL and 1e-40 < |value^power| < 1e40, - * and worse accuracy otherwise. - * - * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). - * @param power A double value. - * @return value^power. - */ - public static double powQuick(double value, double power) { - if (USE_JDK_MATH) { - return Math.pow(value,power); - } - return exp(power*logQuick(value)); - } - - /** - * This treatment is somehow accurate for low values of |power|, - * and for |power*getExponent(value)| < 1023 or so (to stay away - * from double extreme magnitudes (large and small)). - * - * @param value A double value. - * @param power A power. - * @return value^power. - */ - public static double powFast(double value, int power) { - if (USE_JDK_MATH) { - return Math.pow(value,power); - } - if (power < 3) { - if (power < 0) { - // Opposite of Integer.MIN_VALUE does not exist as int. - if (power == Integer.MIN_VALUE) { - // Integer.MAX_VALUE = -(power+1) - return 1.0/(powFast(value,Integer.MAX_VALUE) * value); - } else { - return 1.0/powFast(value,-power); - } - } else { - // Here, power is in [0,2]. - if (power == 2) { // Most common case first. - return value * value; - } else if (power == 0) { - return 1.0; - } else { // power == 1 - return value; - } - } - } else { // power >= 4 - double oddRemains = 1.0; - // If power <= 5, faster to finish outside the loop. - while (power > 5) { - // Test if power is odd. - if ((power & 1) != 0) { - oddRemains *= value; - } - value *= value; - power >>= 1; // power = power / 2 - } - // Here, power is in [3,5]. - if (power == 3) { - return oddRemains * value * value * value; - } else { // power in [4,5]. - double v2 = value * value; - if (power == 4) { - return oddRemains * v2 * v2; - } else { // power == 5 - return oddRemains * v2 * v2 * value; - } - } - } - } - - /** - * @param value A float value. - * @return value*value. - */ - public static float pow2(float value) { - return value*value; - } - - /** - * @param value A double value. - * @return value*value. - */ - public static double pow2(double value) { - return value*value; - } - - /** - * @param value A float value. - * @return value*value*value. - */ - public static float pow3(float value) { - return value*value*value; - } - - /** - * @param value A double value. - * @return value*value*value. - */ - public static double pow3(double value) { - return value*value*value; - } - - /* - * roots - */ - - /** - * @param value A double value. - * @return Value square root. - */ - public static double sqrt(double value) { - if (USE_JDK_MATH || (!USE_REDEFINED_SQRT)) { - return Math.sqrt(value); - } - // See cbrt for comments, sqrt uses the same ideas. - - if (!(value > 0.0)) { // value <= 0.0, or value is NaN - if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { - return (value < 0.0) ? Double.NaN : value; - } else { - return (value == 0.0) ? value : Double.NaN; - } - } else if (value == Double.POSITIVE_INFINITY) { - return Double.POSITIVE_INFINITY; - } - - double h; - if (value < DOUBLE_MIN_NORMAL) { - value *= TWO_POW_52; - h = 2*TWO_POW_N26; - } else { - h = 2.0; - } - - int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); - int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); - int xIndex = ((valueBitsHi<<12)>>>(32-SQRT_LO_BITS)); - - double result = MyTSqrt.sqrtXSqrtHiTab[valueExponentIndex] * MyTSqrt.sqrtXSqrtLoTab[xIndex]; - double slope = MyTSqrt.sqrtSlopeHiTab[valueExponentIndex] * MyTSqrt.sqrtSlopeLoTab[xIndex]; - value *= 0.25; - - result += (value - result * result) * slope; - result += (value - result * result) * slope; - return h*(result + (value - result * result) * slope); - } - - /** - * Quick sqrt, with with a max relative error of about 3.41e-2 - * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse - * accuracy outside this range. - * - * @param value A double value. - * @return Value square root. - */ - public static double sqrtQuick(double value) { - if (USE_JDK_MATH) { - return Math.sqrt(value); - } - final long bits = Double.doubleToRawLongBits(value); - /* - * Constant determined empirically, using a random-based metaheuristic. - * Should be possible to find a better one. - */ - return Double.longBitsToDouble((bits+4606859074900000000L)>>>1); - } - - /** - * Quick inverse of square root, with a max relative error of about 3.44e-2 - * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse accuracy - * outside this range. - * - * This implementation uses zero step of Newton's method. - * Here are the max relative errors on [Double.MIN_NORMAL,Double.MAX_VALUE] - * depending on number of steps, if you want to copy-paste this code - * and use your own number: - * n=0: about 3.44e-2 - * n=1: about 1.75e-3 - * n=2: about 4.6e-6 - * n=3: about 3.17e-11 - * n=4: about 3.92e-16 - * n=5: about 3.03e-16 - * - * @param value A double value. - * @return Inverse of value square root. - */ - public static double invSqrtQuick(double value) { - if (USE_JDK_MATH) { - return 1/Math.sqrt(value); - } - /* - * http://en.wikipedia.org/wiki/Fast_inverse_square_root - */ - if (false) { - // With one Newton step (much slower than - // 1/Math.sqrt(double) if not optimized). - final double halfInitial = value * 0.5; - long bits = Double.doubleToRawLongBits(value); - // If n=0, 6910474759270000000L might be better (3.38e-2 max relative error). - bits = 0x5FE6EB50C7B537A9L - (bits>>1); - value = Double.longBitsToDouble(bits); - value = value * (1.5 - halfInitial * value * value); // Newton step, can repeat. - return value; - } else { - return Double.longBitsToDouble(0x5FE6EB50C7B537A9L - (Double.doubleToRawLongBits(value)>>1)); - } - } - - /** - * @param value A double value. - * @return Value cubic root. - */ - public static double cbrt(double value) { - if (USE_JDK_MATH) { - return Math.cbrt(value); - } - double h; - if (value < 0.0) { - if (value == Double.NEGATIVE_INFINITY) { - return Double.NEGATIVE_INFINITY; - } - value = -value; - // Making sure value is normal. - if (value < DOUBLE_MIN_NORMAL) { - value *= (TWO_POW_52*TWO_POW_26); - // h = * / - h = -2*TWO_POW_N26; - } else { - h = -2.0; - } - } else { - if (!(value < Double.POSITIVE_INFINITY)) { // value is +Infinity, or value is NaN - return value; - } - // Making sure value is normal. - if (value < DOUBLE_MIN_NORMAL) { - if (value == 0.0) { - // cbrt(0.0) = 0.0, cbrt(-0.0) = -0.0 - return value; - } - value *= (TWO_POW_52*TWO_POW_26); - h = 2*TWO_POW_N26; - } else { - h = 2.0; - } - } - - // Normal value is (2^ * ). - // First member cubic root is computed, and multiplied with an approximation - // of the cubic root of the second member, to end up with a good guess of - // the result before using Newton's (or Archimedes's) method. - // To compute the cubic root approximation, we use the formula "cbrt(value) = cbrt(x) * cbrt(value/x)", - // choosing x as close to value as possible but inferior to it, so that cbrt(value/x) is close to 1 - // (we could iterate on this method, using value/x as new value for each iteration, - // but finishing with Newton's method is faster). - - // Shift and cast into an int, which overall is faster than working with a long. - int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); - int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); - // Getting the first CBRT_LO_BITS bits of the mantissa. - int xIndex = ((valueBitsHi<<12)>>>(32-CBRT_LO_BITS)); - double result = MyTCbrt.cbrtXCbrtHiTab[valueExponentIndex] * MyTCbrt.cbrtXCbrtLoTab[xIndex]; - double slope = MyTCbrt.cbrtSlopeHiTab[valueExponentIndex] * MyTCbrt.cbrtSlopeLoTab[xIndex]; - - // Lowering values to avoid overflows when using Newton's method - // (we will then just have to return twice the result). - // result^3 = value - // (result/2)^3 = value/8 - value *= 0.125; - // No need to divide result here, as division is factorized in result computation tables. - // result *= 0.5; - - // Newton's method, looking for y = x^(1/p): - // y(n) = y(n-1) + (x-y(n-1)^p) * slope(y(n-1)) - // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^(1/p-1)) - // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^((1-p)/p)) - // with x(n-1)=y(n-1)^p, i.e.: - // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(y(n-1)^(1-p)) - // - // For p=3: - // y(n) = y(n-1) + (x-y(n-1)^3) * (1/(3*y(n-1)^2)) - - // To save time, we don't recompute the slope between Newton's method steps, - // as initial slope is good enough for a few iterations. - // - // NB: slope = 1/(3*trueResult*trueResult) - // As we have result = trueResult/2 (to avoid overflows), we have: - // slope = 4/(3*result*result) - // = (4/3)*resultInv*resultInv - // with newResultInv = 1/newResult - // = 1/(oldResult+resultDelta) - // = (oldResultInv)*1/(1+resultDelta/oldResult) - // = (oldResultInv)*1/(1+resultDelta*oldResultInv) - // ~= (oldResultInv)*(1-resultDelta*oldResultInv) - // ===> Successive slopes could be computed without division, if needed, - // by computing resultInv (instead of slope right away) and retrieving - // slopes from it. - - result += (value - result * result * result) * slope; - result += (value - result * result * result) * slope; - return h*(result + (value - result * result * result) * slope); - } - - /** - * @return sqrt(x^2+y^2) without intermediate overflow or underflow. - */ - public static double hypot(double x, double y) { - if (USE_JDK_MATH) { - return Math.hypot(x,y); - } - x = Math.abs(x); - y = Math.abs(y); - // Ensuring x <= y. - if (y < x) { - double a = x; - x = y; - y = a; - } else if (!(y >= x)) { // Testing if we have some NaN. - return hypot_NaN(x, y); - } - - if (y-x == y) { - // x too small to subtract from y. - return y; - } else { - double factor; - if (y > HYPOT_MAX_MAG) { - // y is too large: scaling down. - x *= (1/HYPOT_FACTOR); - y *= (1/HYPOT_FACTOR); - factor = HYPOT_FACTOR; - } else if (x < (1/HYPOT_MAX_MAG)) { - // x is too small: scaling up. - x *= HYPOT_FACTOR; - y *= HYPOT_FACTOR; - factor = (1/HYPOT_FACTOR); - } else { - factor = 1.0; - } - return factor * sqrt(x*x+y*y); - } - } - - /** - * @return sqrt(x^2+y^2+z^2) without intermediate overflow or underflow. - */ - public static double hypot(double x, double y, double z) { - if (USE_JDK_MATH) { - // No simple JDK equivalent. - } - x = Math.abs(x); - y = Math.abs(y); - z = Math.abs(z); - /* - * Considering that z magnitude is the most likely to be the smaller, - * hence ensuring z <= y <= x, and not x <= y <= z, for less swaps. - */ - // Ensuring z <= y. - if (z > y) { - // y < z: swapping y and z - double a = z; - z = y; - y = a; - } else if (!(z <= y)) { // Testing if y or z is NaN. - return hypot_NaN(x, y, z); - } - // Ensuring y <= x. - if (z > x) { - // x < z <= y: moving x - double oldZ = z; - z = x; - double oldY = y; - y = oldZ; - x = oldY; - } else if (y > x) { - // z <= x < y: swapping x and y - double a = y; - y = x; - x = a; - } else if (x != x) { // Testing if x is NaN. - return hypot_NaN(x, y, z); - } - - if (x-y == x) { - // y, hence z, too small to subtract from x. - return x; - } else if (y-z == y) { - // z too small to subtract from y, hence x. - double factor; - if (x > HYPOT_MAX_MAG) { - // x is too large: scaling down. - x *= (1/HYPOT_FACTOR); - y *= (1/HYPOT_FACTOR); - factor = HYPOT_FACTOR; - } else if (y < (1/HYPOT_MAX_MAG)) { - // y is too small: scaling up. - x *= HYPOT_FACTOR; - y *= HYPOT_FACTOR; - factor = (1/HYPOT_FACTOR); - } else { - factor = 1.0; - } - return factor * sqrt(x*x+y*y); - } else { - double factor; - if (x > HYPOT_MAX_MAG) { - // x is too large: scaling down. - x *= (1/HYPOT_FACTOR); - y *= (1/HYPOT_FACTOR); - z *= (1/HYPOT_FACTOR); - factor = HYPOT_FACTOR; - } else if (z < (1/HYPOT_MAX_MAG)) { - // z is too small: scaling up. - x *= HYPOT_FACTOR; - y *= HYPOT_FACTOR; - z *= HYPOT_FACTOR; - factor = (1/HYPOT_FACTOR); - } else { - factor = 1.0; - } - // Adding smaller magnitudes together first. - return factor * sqrt(x*x+(y*y+z*z)); - } - } - - /* - * close values - */ - - /** - * @param value A float value. - * @return Floor of value. - */ - public static float floor(float value) { - final int exponent = getExponent(value); - if (exponent < 0) { - // abs(value) < 1. - if (value < 0.0f) { - return -1.0f; - } else { - // 0.0f, or -0.0f if value is -0.0f - return 0.0f * value; - } - } else if (exponent < 23) { - // A bit faster than using casts. - final int bits = Float.floatToRawIntBits(value); - final int anteCommaBits = bits & (0xFF800000>>exponent); - if ((value < 0.0f) && (anteCommaBits != bits)) { - return Float.intBitsToFloat(anteCommaBits) - 1.0f; - } else { - return Float.intBitsToFloat(anteCommaBits); - } - } else { - // +-Infinity, NaN, or a mathematical integer. - return value; - } - } - - /** - * @param value A double value. - * @return Floor of value. - */ - public static double floor(double value) { - if (USE_JDK_MATH) { - return Math.floor(value); - } - if (ANTI_SLOW_CASTS) { - double valueAbs = Math.abs(value); - if (valueAbs <= (double)Integer.MAX_VALUE) { - if (value > 0.0) { - return (double)(int)value; - } else if (value < 0.0) { - double anteCommaDigits = (double)(int)value; - if (value != anteCommaDigits) { - return anteCommaDigits - 1.0; - } else { - return anteCommaDigits; - } - } else { // value is +-0.0 (not NaN due to test against Integer.MAX_VALUE) - return value; - } - } else if (valueAbs < TWO_POW_52) { - // We split the value in two: - // high part, which is a mathematical integer, - // and the rest, for which we can get rid of the - // post comma digits by casting into an int. - double highPart = ((int)(value * TWO_POW_N26)) * TWO_POW_26; - if (value > 0.0) { - return highPart + (double)((int)(value - highPart)); - } else { - double anteCommaDigits = highPart + (double)((int)(value - highPart)); - if (value != anteCommaDigits) { - return anteCommaDigits - 1.0; - } else { - return anteCommaDigits; - } - } - } else { // abs(value) >= 2^52, or value is NaN - return value; - } - } else { - final int exponent = getExponent(value); - if (exponent < 0) { - // abs(value) < 1. - if (value < 0.0) { - return -1.0; - } else { - // 0.0, or -0.0 if value is -0.0 - return 0.0 * value; - } - } else if (exponent < 52) { - // A bit faster than working on bits. - final long matIntPart = (long)value; - final double matIntToValue = value-(double)matIntPart; - if (matIntToValue >= 0.0) { - return (double)matIntPart; - } else { - return (double)(matIntPart - 1); - } - } else { - // +-Infinity, NaN, or a mathematical integer. - return value; - } - } - } - - /** - * @param value A float value. - * @return Ceiling of value. - */ - public static float ceil(float value) { - return -floor(-value); - } - - /** - * @param value A double value. - * @return Ceiling of value. - */ - public static double ceil(double value) { - if (USE_JDK_MATH) { - return Math.ceil(value); - } - return -floor(-value); - } - - /** - * Might have different semantics than Math.round(float), - * see bugs 6430675 and 8010430. - * - * @param value A double value. - * @return Value rounded to nearest int, choosing superior int in case two - * are equally close (i.e. rounding-up). - */ - public static int round(float value) { - // Algorithm by Dmitry Nadezhin - // (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html). - final int bits = Float.floatToRawIntBits(value); - final int biasedExp = ((bits>>23)&0xFF); - // Shift to get rid of bits past comma except first one: will need to - // 1-shift to the right to end up with correct magnitude. - final int shift = (23 - 1 + MAX_FLOAT_EXPONENT) - biasedExp; - if ((shift & -32) == 0) { - // shift in [0,31], so unbiased exp in [-9,22]. - int extendedMantissa = (0x00800000 | (bits & 0x007FFFFF)); - if (bits < 0) { - extendedMantissa = -extendedMantissa; - } - // If value is positive and first bit past comma is 0, rounding - // to lower integer, else to upper one, which is what "+1" and - // then ">>1" do. - return ((extendedMantissa >> shift) + 1) >> 1; - } else { - // +-Infinity, NaN, or a mathematical integer. - if (false && ANTI_SLOW_CASTS) { // not worth it - if (Math.abs(value) >= -(float)Integer.MIN_VALUE) { - // +-Infinity or a mathematical integer (mostly) out of int range. - return (value < 0.0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; - } - // NaN or a mathematical integer (mostly) in int range. - } - return (int)value; - } - } - - /** - * Might have different semantics than Math.round(double), - * see bugs 6430675 and 8010430. - * - * @param value A double value. - * @return Value rounded to nearest long, choosing superior long in case two - * are equally close (i.e. rounding-up). - */ - public static long round(double value) { - final long bits = Double.doubleToRawLongBits(value); - final int biasedExp = (((int)(bits>>52))&0x7FF); - // Shift to get rid of bits past comma except first one: will need to - // 1-shift to the right to end up with correct magnitude. - final int shift = (52 - 1 + MAX_DOUBLE_EXPONENT) - biasedExp; - if ((shift & -64) == 0) { - // shift in [0,63], so unbiased exp in [-12,51]. - long extendedMantissa = (0x0010000000000000L | (bits & 0x000FFFFFFFFFFFFFL)); - if (bits < 0) { - extendedMantissa = -extendedMantissa; - } - // If value is positive and first bit past comma is 0, rounding - // to lower integer, else to upper one, which is what "+1" and - // then ">>1" do. - return ((extendedMantissa >> shift) + 1L) >> 1; - } else { - // +-Infinity, NaN, or a mathematical integer. - if (ANTI_SLOW_CASTS) { - if (Math.abs(value) >= -(double)Long.MIN_VALUE) { - // +-Infinity or a mathematical integer (mostly) out of long range. - return (value < 0.0) ? Long.MIN_VALUE : Long.MAX_VALUE; - } - // NaN or a mathematical integer (mostly) in long range. - } - return (long)value; - } - } - - /** - * @param value A float value. - * @return Value rounded to nearest int, choosing even int in case two - * are equally close. - */ - public static int roundEven(float value) { - final int sign = signFromBit(value); - value = Math.abs(value); - if (ANTI_SLOW_CASTS) { - if (value < TWO_POW_23_F) { - // Getting rid of post-comma bits. - value = ((value + TWO_POW_23_F) - TWO_POW_23_F); - return sign * (int)value; - } else if (value < (float)Integer.MAX_VALUE) { // <= doesn't work because of float precision - // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] - return sign * (int)value; - } - } else { - if (value < TWO_POW_23_F) { - // Getting rid of post-comma bits. - value = ((value + TWO_POW_23_F) - TWO_POW_23_F); - } - } - return (int)(sign * value); - } - - /** - * @param value A double value. - * @return Value rounded to nearest long, choosing even long in case two - * are equally close. - */ - public static long roundEven(double value) { - final int sign = (int)signFromBit(value); - value = Math.abs(value); - if (value < TWO_POW_52) { - // Getting rid of post-comma bits. - value = ((value + TWO_POW_52) - TWO_POW_52); - } - if (ANTI_SLOW_CASTS) { - if (value <= (double)Integer.MAX_VALUE) { - // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] - return sign * (int)value; - } - } - return (long)(sign * value); - } - - /** - * @param value A float value. - * @return The float mathematical integer closest to the specified value, - * choosing even one if two are equally close, or respectively - * NaN, +-Infinity or +-0.0f if the value is any of these. - */ - public static float rint(float value) { - final int sign = signFromBit(value); - value = Math.abs(value); - if (value < TWO_POW_23_F) { - // Getting rid of post-comma bits. - value = ((TWO_POW_23_F + value ) - TWO_POW_23_F); - } - // Restoring original sign. - return sign * value; - } - - /** - * @param value A double value. - * @return The double mathematical integer closest to the specified value, - * choosing even one if two are equally close, or respectively - * NaN, +-Infinity or +-0.0 if the value is any of these. - */ - public static double rint(double value) { - if (USE_JDK_MATH) { - return Math.rint(value); - } - final int sign = (int)signFromBit(value); - value = Math.abs(value); - if (value < TWO_POW_52) { - // Getting rid of post-comma bits. - value = ((TWO_POW_52 + value ) - TWO_POW_52); - } - // Restoring original sign. - return sign * value; - } - - /* - * ranges - */ - - /** - * @param min A float value. - * @param max A float value. - * @param value A float value. - * @return min if value < min, max if value > max, value otherwise. - */ - public static float toRange(float min, float max, float value) { - return NumbersUtils.toRange(min, max, value); - } - - /** - * @param min A double value. - * @param max A double value. - * @param value A double value. - * @return min if value < min, max if value > max, value otherwise. - */ - public static double toRange(double min, double max, double value) { - return NumbersUtils.toRange(min, max, value); - } - - /* - * binary operators (/,%) - */ - - /** - * Returns dividend - divisor * n, where n is the mathematical integer - * closest to dividend/divisor. - * If dividend/divisor is equally close to surrounding integers, - * we choose n to be the integer of smallest magnitude, which makes - * this treatment differ from Math.IEEEremainder(double,double), - * where n is chosen to be the even integer. - * Note that the choice of n is not done considering the double - * approximation of dividend/divisor, because it could cause - * result to be outside [-|divisor|/2,|divisor|/2] range. - * The practical effect is that if multiple results would be possible, - * we always choose the result that is the closest to (and has the same - * sign as) the dividend. - * Ex. : - * - for (-3.0,2.0), this method returns -1.0, - * whereas Math.IEEEremainder returns 1.0. - * - for (-5.0,2.0), both this method and Math.IEEEremainder return -1.0. - * - * If the remainder is zero, its sign is the same as the sign of the first argument. - * If either argument is NaN, or the first argument is infinite, - * or the second argument is positive zero or negative zero, - * then the result is NaN. - * If the first argument is finite and the second argument is - * infinite, then the result is the same as the first argument. - * - * NB: - * - Modulo operator (%) returns a value in ]-|divisor|,|divisor|[, - * which sign is the same as dividend. - * - As for modulo operator, the sign of the divisor has no effect on the result. - * - On some architecture, % operator has been observed to return NaN - * for some subnormal values of divisor, when dividend exponent is 1023, - * which impacts the correctness of this method. - * - * @param dividend Dividend. - * @param divisor Divisor. - * @return Remainder of dividend/divisor, i.e. a value in [-|divisor|/2,|divisor|/2]. - */ - public static double remainder(double dividend, double divisor) { - if (Double.isInfinite(divisor)) { - if (Double.isInfinite(dividend)) { - return Double.NaN; - } else { - return dividend; - } - } - double value = dividend % divisor; - if (Math.abs(value+value) > Math.abs(divisor)) { - return value + ((value > 0.0) ? -Math.abs(divisor) : Math.abs(divisor)); - } else { - return value; - } - } - - /** - * @param angle Angle in radians. - * @return The same angle, in radians, but in [-PI,PI]. - */ - public static double normalizeMinusPiPi(double angle) { - // Not modifying values in output range. - if ((angle >= -Math.PI) && (angle <= Math.PI)) { - return angle; - } - return remainderTwoPi(angle); - } - - /** - * Not accurate for large values. - * - * @param angle Angle in radians. - * @return The same angle, in radians, but in [-PI,PI]. - */ - public static double normalizeMinusPiPiFast(double angle) { - // Not modifying values in output range. - if ((angle >= -Math.PI) && (angle <= Math.PI)) { - return angle; - } - return remainderTwoPiFast(angle); - } - - /** - * @param angle Angle in radians. - * @return The same angle, in radians, but in [0,2*PI]. - */ - public static double normalizeZeroTwoPi(double angle) { - // Not modifying values in output range. - if ((angle >= 0.0) && (angle <= 2*Math.PI)) { - return angle; - } - angle = remainderTwoPi(angle); - if (angle < 0.0) { - // LO then HI is theoretically better (when starting near 0). - return (angle + TWOPI_LO) + TWOPI_HI; - } else { - return angle; - } - } - - /** - * Not accurate for large values. - * - * @param angle Angle in radians. - * @return The same angle, in radians, but in [0,2*PI]. - */ - public static double normalizeZeroTwoPiFast(double angle) { - // Not modifying values in output range. - if ((angle >= 0.0) && (angle <= 2*Math.PI)) { - return angle; - } - angle = remainderTwoPiFast(angle); - if (angle < 0.0) { - // LO then HI is theoretically better (when starting near 0). - return (angle + TWOPI_LO) + TWOPI_HI; - } else { - return angle; - } - } - - /** - * @param angle Angle in radians. - * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. - */ - public static double normalizeMinusHalfPiHalfPi(double angle) { - // Not modifying values in output range. - if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { - return angle; - } - return remainderPi(angle); - } - - /** - * Not accurate for large values. - * - * @param angle Angle in radians. - * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. - */ - public static double normalizeMinusHalfPiHalfPiFast(double angle) { - // Not modifying values in output range. - if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { - return angle; - } - return remainderPiFast(angle); - } - - /* - * floating points utils - */ - - /** - * @param value A float value. - * @return true if the specified value is NaN or +-Infinity, false otherwise. - */ - public static boolean isNaNOrInfinite(float value) { - return NumbersUtils.isNaNOrInfinite(value); - } - - /** - * @param value A double value. - * @return true if the specified value is NaN or +-Infinity, false otherwise. - */ - public static boolean isNaNOrInfinite(double value) { - return NumbersUtils.isNaNOrInfinite(value); - } - - /** - * @param value A float value. - * @return Value unbiased exponent. - */ - public static int getExponent(float value) { - return ((Float.floatToRawIntBits(value)>>23)&0xFF)-MAX_FLOAT_EXPONENT; - } - - /** - * @param value A double value. - * @return Value unbiased exponent. - */ - public static int getExponent(double value) { - return (((int)(Double.doubleToRawLongBits(value)>>52))&0x7FF)-MAX_DOUBLE_EXPONENT; - } - - /** - * @param value A float value. - * @return -1.0f if the specified value is < 0, 1.0f if it is > 0, - * and the value itself if it is NaN or +-0.0f. - */ - public static float signum(float value) { - if (USE_JDK_MATH) { - return Math.signum(value); - } - if ((value == 0.0f) || (value != value)) { - return value; - } - return (float)signFromBit(value); - } - - /** - * @param value A double value. - * @return -1.0 if the specified value is < 0, 1.0 if it is > 0, - * and the value itself if it is NaN or +-0.0. - */ - public static double signum(double value) { - if (USE_JDK_MATH) { - return Math.signum(value); - } - if ((value == 0.0) || (value != value)) { - return value; - } - if (ANTI_SLOW_CASTS) { - return (double)(int)signFromBit(value); - } else { - return (double)signFromBit(value); - } - } - - /** - * @param value A float value. - * @return -1 if sign bit is 1, 1 if sign bit is 0. - */ - public static int signFromBit(float value) { - return ((Float.floatToRawIntBits(value)>>30)|1); - } - - /** - * @param value A double value. - * @return -1 if sign bit is 1, 1 if sign bit is 0. - */ - public static long signFromBit(double value) { - // Returning a long, to avoid useless cast into int. - return ((Double.doubleToRawLongBits(value)>>62)|1); - } - - /** - * A sign of NaN can be interpreted as positive or negative. - * - * @param magnitude A float value. - * @param sign A float value. - * @return A value with the magnitude of the first argument, and the sign - * of the second argument. - */ - public static float copySign(float magnitude, float sign) { - return Float.intBitsToFloat( - (Float.floatToRawIntBits(sign) & Integer.MIN_VALUE) - | (Float.floatToRawIntBits(magnitude) & Integer.MAX_VALUE)); - } - - /** - * A sign of NaN can be interpreted as positive or negative. - * - * @param magnitude A double value. - * @param sign A double value. - * @return A value with the magnitude of the first argument, and the sign - * of the second argument. - */ - public static double copySign(double magnitude, double sign) { - return Double.longBitsToDouble( - (Double.doubleToRawLongBits(sign) & Long.MIN_VALUE) - | (Double.doubleToRawLongBits(magnitude) & Long.MAX_VALUE)); - } - - /** - * The ULP (Unit in the Last Place) is the distance to the next value larger - * in magnitude. - * - * @param value A float value. - * @return The size of an ulp of the specified value, or Float.MIN_VALUE - * if it is +-0.0f, or +Infinity if it is +-Infinity, or NaN - * if it is NaN. - */ - public static float ulp(float value) { - if (USE_JDK_MATH) { - return Math.ulp(value); - } - /* - * Look-up table not really worth it in micro-benchmark, - * so should be worse with cache-misses. - */ - final int exponent = getExponent(value); - if (exponent >= (MIN_FLOAT_NORMAL_EXPONENT+23)) { - if (exponent == MAX_FLOAT_EXPONENT+1) { - // NaN or +-Infinity - return Math.abs(value); - } - // normal: returning 2^(exponent-23) - return Float.intBitsToFloat((exponent+(MAX_FLOAT_EXPONENT-23))<<23); - } else { - if (exponent == MIN_FLOAT_NORMAL_EXPONENT-1) { - // +-0.0f or subnormal - return Float.MIN_VALUE; - } - // subnormal result - return Float.intBitsToFloat(1<<(exponent-MIN_FLOAT_NORMAL_EXPONENT)); - } - } - - /** - * The ULP (Unit in the Last Place) is the distance to the next value larger - * in magnitude. - * - * @param value A double value. - * @return The size of an ulp of the specified value, or Double.MIN_VALUE - * if it is +-0.0, or +Infinity if it is +-Infinity, or NaN - * if it is NaN. - */ - public static double ulp(double value) { - if (USE_JDK_MATH) { - return Math.ulp(value); - } - /* - * Look-up table not really worth it in micro-benchmark, - * so should be worse with cache-misses. - */ - final int exponent = getExponent(value); - if (exponent >= (MIN_DOUBLE_NORMAL_EXPONENT+52)) { - if (exponent == MAX_DOUBLE_EXPONENT+1) { - // NaN or +-Infinity - return Math.abs(value); - } - // normal: returning 2^(exponent-52) - return Double.longBitsToDouble((exponent+(MAX_DOUBLE_EXPONENT-52L))<<52); - } else { - if (exponent == MIN_DOUBLE_NORMAL_EXPONENT-1) { - // +-0.0f or subnormal - return Double.MIN_VALUE; - } - // subnormal result - return Double.longBitsToDouble(1L<<(exponent-MIN_DOUBLE_NORMAL_EXPONENT)); - } - } - - /** - * If both arguments are +-0.0(f), (float)direction is returned. - * - * If both arguments are +Infinity or -Infinity, - * respectively +Infinity or -Infinity is returned. - * - * @param start A float value. - * @param direction A double value. - * @return The float adjacent to start towards direction, considering that - * +(-)Float.MIN_VALUE is adjacent to +(-)0.0f, and that - * +(-)Float.MAX_VALUE is adjacent to +(-)Infinity, - * or NaN if any argument is NaN. - */ - public static float nextAfter(float start, double direction) { - if (direction < start) { - // Going towards -Infinity. - if (start == 0.0f) { - // +-0.0f - return -Float.MIN_VALUE; - } - final int bits = Float.floatToRawIntBits(start); - return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); - } else if (direction > start) { - // Going towards +Infinity. - // +0.0f to get rid of eventual -0.0f - final int bits = Float.floatToRawIntBits(start + 0.0f); - return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); - } else if (start == direction) { - return (float)direction; - } else { - // Returning a NaN derived from the input NaN(s). - return start + (float)direction; - } - } - - /** - * If both arguments are +-0.0, direction is returned. - * - * If both arguments are +Infinity or -Infinity, - * respectively +Infinity or -Infinity is returned. - * - * @param start A double value. - * @param direction A double value. - * @return The double adjacent to start towards direction, considering that - * +(-)Double.MIN_VALUE is adjacent to +(-)0.0, and that - * +(-)Double.MAX_VALUE is adjacent to +(-)Infinity, - * or NaN if any argument is NaN. - */ - public static double nextAfter(double start, double direction) { - if (direction < start) { - // Going towards -Infinity. - if (start == 0.0) { - // +-0.0 - return -Double.MIN_VALUE; - } - final long bits = Double.doubleToRawLongBits(start); - return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); - } else if (direction > start) { - // Going towards +Infinity. - // +0.0 to get rid of eventual -0.0 - final long bits = Double.doubleToRawLongBits(start + 0.0f); - return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); - } else if (start == direction) { - return direction; - } else { - // Returning a NaN derived from the input NaN(s). - return start + direction; - } - } - - /** - * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). - */ - public static float nextDown(float start) { - if (start > Float.NEGATIVE_INFINITY) { - if (start == 0.0f) { - // +-0.0f - return -Float.MIN_VALUE; - } - final int bits = Float.floatToRawIntBits(start); - return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); - } else if (start == Float.NEGATIVE_INFINITY) { - return Float.NEGATIVE_INFINITY; - } else { - // NaN - return start; - } - } - - /** - * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). - */ - public static double nextDown(double start) { - if (start > Double.NEGATIVE_INFINITY) { - if (start == 0.0) { - // +-0.0 - return -Double.MIN_VALUE; - } - final long bits = Double.doubleToRawLongBits(start); - return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); - } else if (start == Double.NEGATIVE_INFINITY) { - return Double.NEGATIVE_INFINITY; - } else { - // NaN - return start; - } - } - - /** - * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). - */ - public static float nextUp(float start) { - if (start < Float.POSITIVE_INFINITY) { - // +0.0f to get rid of eventual -0.0f - final int bits = Float.floatToRawIntBits(start + 0.0f); - return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); - } else if (start == Float.POSITIVE_INFINITY) { - return Float.POSITIVE_INFINITY; - } else { - // NaN - return start; - } - } - - /** - * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). - */ - public static double nextUp(double start) { - if (start < Double.POSITIVE_INFINITY) { - // +0.0 to get rid of eventual -0.0 - final long bits = Double.doubleToRawLongBits(start + 0.0); - return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); - } else if (start == Double.POSITIVE_INFINITY) { - return Double.POSITIVE_INFINITY; - } else { - // NaN - return start; - } - } - - /** - * Precision may be lost if the result is subnormal. - * - * @param value A float value. - * @param scaleFactor An int value. - * @return value * 2^scaleFactor, or a value equivalent to the specified - * one if it is NaN, +-Infinity or +-0.0f. - */ - public static float scalb(float value, int scaleFactor) { - // Large enough to imply overflow or underflow for - // a finite non-zero value. - final int MAX_SCALE = 2*MAX_FLOAT_EXPONENT+23+1; - - // Making sure scaling factor is in a reasonable range. - scaleFactor = Math.max(Math.min(scaleFactor, MAX_SCALE), -MAX_SCALE); - - return (float)(((double)value) * twoPowNormal(scaleFactor)); - } - - /** - * Precision may be lost if the result is subnormal. - * - * @param value A double value. - * @param scaleFactor An int value. - * @return value * 2^scaleFactor, or a value equivalent to the specified - * one if it is NaN, +-Infinity or +-0.0. - */ - public static double scalb(double value, int scaleFactor) { - if ((scaleFactor > -MAX_DOUBLE_EXPONENT) && (scaleFactor <= MAX_DOUBLE_EXPONENT)) { - // Quick case (as done in apache FastMath). - return value * twoPowNormal(scaleFactor); - } - - // Large enough to imply overflow or underflow for - // a finite non-zero value. - final int MAX_SCALE = 2*MAX_DOUBLE_EXPONENT+52+1; - - // Making sure scaling factor is in a reasonable range. - final int exponentAdjust; - final int scaleIncrement; - final double exponentDelta; - if (scaleFactor < 0) { - scaleFactor = Math.max(scaleFactor, -MAX_SCALE); - scaleIncrement = -512; - exponentDelta = TWO_POW_N512; - } else { - scaleFactor = Math.min(scaleFactor, MAX_SCALE); - scaleIncrement = 512; - exponentDelta = TWO_POW_512; - } - - // Calculating (scaleFactor % +-512), 512 = 2^9, using - // technique from "Hacker's Delight" section 10-2. - final int t = ((scaleFactor >> (9-1)) >>> (32-9)); - exponentAdjust = ((scaleFactor + t) & (512-1)) - t; - - value *= twoPowNormal(exponentAdjust); - scaleFactor -= exponentAdjust; - - while (scaleFactor != 0) { - value *= exponentDelta; - scaleFactor -= scaleIncrement; - } - - return value; - } - - /* - * Non-redefined Math public values and treatments. - */ - - public static float abs(float a) { - return Math.abs(a); - } - - public static double abs(double a) { - return Math.abs(a); - } - - public static float min(float a, float b) { - return Math.min(a,b); - } - - public static double min(double a, double b) { - return Math.min(a,b); - } - - public static float max(float a, float b) { - return Math.max(a,b); - } - - public static double max(double a, double b) { - return Math.max(a,b); - } - - public static double IEEEremainder(double f1, double f2) { - return Math.IEEEremainder(f1,f2); - } - - public static double random() { - return Math.random(); - } - - //-------------------------------------------------------------------------- - // PRIVATE METHODS - //-------------------------------------------------------------------------- - - /** - * Non-instantiable. - */ - private FastMath() { - } - - /* - * Remainders (accurate). - */ - - /** - * @param angle Angle in radians. - * @return Remainder of (angle % (2*PI)), in [-PI,PI]. - */ - private static double remainderTwoPi(double angle) { - if (USE_JDK_MATH) { - return jdkRemainderTwoPi(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle <= (4*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { - double fn = (double)(int)(angle*TWOPI_INV+0.5); - angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; - // Ensuring range. - // HI/LO can help a bit, even though we are always far from 0. - if (angle < -Math.PI) { - angle = (angle + TWOPI_HI) + TWOPI_LO; - } else if (angle > Math.PI) { - angle = (angle - TWOPI_HI) - TWOPI_LO; - } - return negateResult ? -angle : angle; - } else if (angle < Double.POSITIVE_INFINITY) { - angle = heavyRemainderTwoPi(angle); - return negateResult ? -angle : angle; - } else { // angle is +Infinity or NaN - return Double.NaN; - } - } - - /** - * @param angle Angle in radians. - * @return Remainder of (angle % PI), in [-PI/2,PI/2]. - */ - private static double remainderPi(double angle) { - if (USE_JDK_MATH) { - return jdkRemainderPi(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle <= (2*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { - double fn = (double)(int)(angle*PI_INV+0.5); - angle = (angle - fn*PI_HI) - fn*PI_LO; - // Ensuring range. - // HI/LO can help a bit, even though we are always far from 0. - if (angle < -Math.PI/2) { - angle = (angle + PI_HI) + PI_LO; - } else if (angle > Math.PI/2) { - angle = (angle - PI_HI) - PI_LO; - } - return negateResult ? -angle : angle; - } else if (angle < Double.POSITIVE_INFINITY) { - angle = heavyRemainderPi(angle); - return negateResult ? -angle : angle; - } else { // angle is +Infinity or NaN - return Double.NaN; - } - } - - /** - * @param angle Angle in radians. - * @return Bits of double corresponding to remainder of (angle % (PI/2)), - * in [-PI/4,PI/4], with quadrant encoded in exponent bits. - */ - private static long remainderPiO2(double angle) { - if (USE_JDK_MATH) { - return jdkRemainderPiO2(angle, false); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - if (angle <= NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2) { - int n = (int)(angle*PIO2_INV+0.5); - double fn = (double)n; - angle = (angle - fn*PIO2_HI) - fn*PIO2_LO; - // Ensuring range. - // HI/LO can help a bit, even though we are always far from 0. - if (angle < -Math.PI/4) { - angle = (angle + PIO2_HI) + PIO2_LO; - n--; - } else if (angle > Math.PI/4) { - angle = (angle - PIO2_HI) - PIO2_LO; - n++; - } - if (negateResult) { - angle = -angle; - } - return encodeRemainderAndQuadrant(angle, n&3); - } else if (angle < Double.POSITIVE_INFINITY) { - return heavyRemainderPiO2(angle, negateResult); - } else { // angle is +Infinity or NaN - return encodeRemainderAndQuadrant(Double.NaN, 0); - } - } - - /* - * Remainders (fast). - */ - - /** - * Not accurate for large values. - * - * @param angle Angle in radians. - * @return Remainder of (angle % (2*PI)), in [-PI,PI]. - */ - private static double remainderTwoPiFast(double angle) { - if (USE_JDK_MATH) { - return jdkRemainderTwoPi(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - // - We don't bother with values higher than (2*PI*(2^52)), - // since they are spaced by 2*PI or more from each other. - // - For large values, we don't use % because it might be very slow, - // and we split computation in two, because cast from double to int - // with large numbers might be very slow also. - if (angle <= TWO_POW_26*(2*Math.PI)) { - // ok - } else if (angle <= TWO_POW_52*(2*Math.PI)) { - // Computing remainder of angle modulo TWO_POW_26*(2*PI). - double fn = (double)(int)(angle*(TWOPI_INV/TWO_POW_26)+0.5); - angle = (angle - fn*(TWOPI_HI*TWO_POW_26)) - fn*(TWOPI_LO*TWO_POW_26); - // Here, angle is in [-TWO_POW_26*PI,TWO_POW_26*PI], or so. - if (angle < 0.0) { - angle = -angle; - negateResult = !negateResult; - } - } else if (angle < Double.POSITIVE_INFINITY) { - return 0.0; - } else { // angle is +Infinity or NaN - return Double.NaN; - } - - // Computing remainder of angle modulo 2*PI. - double fn = (double)(int)(angle*TWOPI_INV+0.5); - angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; - - // Ensuring range. - // HI/LO can help a bit, even though we are always far from 0. - if (angle < -Math.PI) { - angle = (angle + TWOPI_HI) + TWOPI_LO; - } else if (angle > Math.PI) { - angle = (angle - TWOPI_HI) - TWOPI_LO; - } - return negateResult ? -angle : angle; - } - - /** - * Not accurate for large values. - * - * @param angle Angle in radians. - * @return Remainder of (angle % PI), in [-PI/2,PI/2]. - */ - private static double remainderPiFast(double angle) { - if (USE_JDK_MATH) { - return jdkRemainderPi(angle); - } - boolean negateResult = false; - if (angle < 0.0) { - angle = -angle; - negateResult = true; - } - // - We don't bother with values higher than (PI*(2^52)), - // since they are spaced by PI or more from each other. - // - For large values, we don't use % because it might be very slow, - // and we split computation in two, because cast from double to int - // with large numbers might be very slow also. - if (angle <= TWO_POW_26*Math.PI) { - // ok - } else if (angle <= TWO_POW_52*Math.PI) { - // Computing remainder of angle modulo TWO_POW_26*PI. - double fn = (double)(int)(angle*(PI_INV/TWO_POW_26)+0.5); - angle = (angle - fn*(PI_HI*TWO_POW_26)) - fn*(PI_LO*TWO_POW_26); - // Here, angle is in [-TWO_POW_26*PI/2,TWO_POW_26*PI/2], or so. - if (angle < 0.0) { - angle = -angle; - negateResult = !negateResult; - } - } else if (angle < Double.POSITIVE_INFINITY) { - return 0.0; - } else { // angle is +Infinity or NaN - return Double.NaN; - } - - // Computing remainder of angle modulo PI. - double fn = (double)(int)(angle*PI_INV+0.5); - angle = (angle - fn*PI_HI) - fn*PI_LO; - - // Ensuring range. - // HI/LO can help a bit, even though we are always far from 0. - if (angle < -Math.PI/2) { - angle = (angle + PI_HI) + PI_LO; - } else if (angle > Math.PI/2) { - angle = (angle - PI_HI) - PI_LO; - } - return negateResult ? -angle : angle; - } -} diff --git a/src/main/java/net/jafama/IntWrapper.java b/src/main/java/net/jafama/IntWrapper.java deleted file mode 100644 index a8b616a..0000000 --- a/src/main/java/net/jafama/IntWrapper.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 Jeff Hain - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.jafama; - -public class IntWrapper { - public int value; - @Override - public String toString() { - return Integer.toString(this.value); - } -} diff --git a/src/main/java/net/jafama/LICENSE-2.0.txt b/src/main/java/net/jafama/LICENSE-2.0.txt deleted file mode 100644 index d645695..0000000 --- a/src/main/java/net/jafama/LICENSE-2.0.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/main/java/net/jafama/NumbersUtils.java b/src/main/java/net/jafama/NumbersUtils.java deleted file mode 100644 index 269d80a..0000000 --- a/src/main/java/net/jafama/NumbersUtils.java +++ /dev/null @@ -1,2660 +0,0 @@ -/* - * Copyright 2012-2015 Jeff Hain - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.jafama; - -/** - * Class containing various basic utility methods to deal with numbers. - * This class is meant to be light (no big look-up tables or such). - * - * Check methods return boolean if success, - * for it allows to use them in assertions. - * - * toString methods use capital letters, unlike JDK's toStrings, for it is more - * readable (especially, "l" and "1" can easily be confused with one another). - * - * Some methods have an int version additionally to the long version, - * even though long version could be used instead, for performance reasons, - * either for the methods themselves (if they do computations with ints - * instead of longs), or to be used in an int use case (like methods - * checking whether or not a signed int can fit in such number of bits). - */ -public final class NumbersUtils { - - //-------------------------------------------------------------------------- - // MEMBERS - //-------------------------------------------------------------------------- - - /** - * Double.MIN_NORMAL since Java 6. - */ - public static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308 - - /** - * Float.MIN_NORMAL since Java 6. - */ - public static final float FLOAT_MIN_NORMAL = Float.intBitsToFloat(0x00800000); // 1.17549435E-38f - - private static final int MIN_DOUBLE_EXPONENT = -1074; - private static final int MAX_DOUBLE_EXPONENT = 1023; - - /** - * All possible upper case chars for representing a number as a String. - */ - private final static char[] CHAR_BY_DIGIT; - static { - final char minDecimal = '0'; - final char maxDecimal = '9'; - final int n1 = maxDecimal - minDecimal + 1; - final char minLetter = 'A'; - final char maxLetter = 'Z'; - final int n2 = maxLetter - minLetter + 1; - CHAR_BY_DIGIT = new char[n1+n2]; - int i=0; - for (char c=minDecimal;c<=maxDecimal;c++) { - CHAR_BY_DIGIT[i++] = c; - } - for (char c=minLetter;c<=maxLetter;c++) { - CHAR_BY_DIGIT[i++] = c; - } - } - - /** - * For power-of-two radixes only. - */ - private static final int[] DIV_SHIFT_BY_RADIX; - static { - DIV_SHIFT_BY_RADIX = new int[32+1]; - int shift=1; - for (int radix=2;radix<=32;radix*=2) { - DIV_SHIFT_BY_RADIX[radix] = shift++; - } - } - - private final static int[] MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1]; - private final static int[] MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1]; - static { - for (int radix=Character.MIN_RADIX;radix<=Character.MAX_RADIX;radix++) { - /* - * Brutal but works. - * -1 for the sign. - */ - MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix] = Integer.toString(Integer.MIN_VALUE, radix).length()-1; - MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix] = Long.toString(Long.MIN_VALUE, radix).length()-1; - } - } - - static final double NO_CSN_MIN_BOUND_INCL = 1e-3; - static final double NO_CSN_MAX_BOUND_EXCL = 1e7; - - private static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2 - private static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI - private static final double PI_HI = 2*PIO2_HI; - private static final double PI_LO = 2*PIO2_LO; - private static final double TWOPI_HI = 4*PIO2_HI; - private static final double TWOPI_LO = 4*PIO2_LO; - - //-------------------------------------------------------------------------- - // PUBLIC METHODS - //-------------------------------------------------------------------------- - - /** - * @return True if the specified values are equal or both NaN, false otherwise. - */ - public static boolean equal(float a, float b) { - // Only does one test if a == b. - return (a == b) ? true : ((a != a) && (b != b)); - } - - /** - * @return True if the specified values are equal or both NaN, false otherwise. - */ - public static boolean equal(double a, double b) { - // Only does one test if a == b. - return (a == b) ? true : ((a != a) && (b != b)); - } - - /** - * @return True if the specified value is a mathematical integer, - * false otherwise (which includes NaN and +-Infinity). - */ - public static boolean isMathematicalInteger(float value) { - // Doing magnitude test first, for cast - // might be very slow for huge values. - // It also helps be faster for huge values, - // for which the test with cast always fail. - value = Math.abs(value); - return ((value >= (float)(1<<23) - && (value != Float.POSITIVE_INFINITY))) - || (value == (float)(int)value); - } - - /** - * @return True if the specified value is a mathematical integer, - * false otherwise (which includes NaN and +-Infinity). - */ - public static boolean isMathematicalInteger(double value) { - // Doing magnitude test first, for cast - // might be very slow for huge values. - // It also helps be faster for huge values, - // for which the test with cast always fail. - value = Math.abs(value); - return ((value >= (double)(1L<<52)) - && (value != Double.POSITIVE_INFINITY)) - || (value == (double)(long)value); - } - - /** - * @param value A float value. - * @return True if the specified value is equidistant from two adjacent - * mathematical integers, false otherwise (which includes NaN - * and +-Infinity). - */ - public static boolean isEquidistant(float value) { - if (false) { - // Also works, but slower. - final int bits = Float.floatToRawIntBits(value); - final int exponent = ((bits>>23)&0xFF)-127; - final int nbrOfPostCommaBits = 23 - exponent; - if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 25)) { - // No mantissa bit after comma, or all mantissa bits - // (including implicit 1) are at least one bit away from it. - //System.out.println("can't be"); - return false; - } - final int mantissa = 0x00800000|(bits&0x007FFFFF); - final int postCommaMask = ~((-1)<>52))&0x7FF)-1023; - final int nbrOfPostCommaBits = 52 - exponent; - if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 54)) { - // No mantissa bit after comma, or all mantissa bits - // (including implicit 1) are at least one bit away from it. - return false; - } - final long mantissa = 0x0010000000000000L|(bits&0x000FFFFFFFFFFFFFL); - final long postCommaMask = ~((-1L)< a is NaN or +-Infinity - return !(a-a == 0.0f); - } - - /** - * @param value A double value. - * @return True if the specified value is NaN or +-Infinity, false otherwise. - */ - public static boolean isNaNOrInfinite(double a) { - // a-a is not equal to 0.0 (and is NaN) <-> a is NaN or +-Infinity - return !(a-a == 0.0); - } - - /** - * @param value A float value. - * @return -1 if sign bit is 1, 1 if sign bit is 0. - */ - public static int signFromBit(float value) { - return ((Float.floatToRawIntBits(value)>>30)|1); - } - - /** - * @param value A double value. - * @return -1 if sign bit is 1, 1 if sign bit is 0. - */ - public static long signFromBit(double value) { - // Returning a long, to avoid useless cast into int. - return ((Double.doubleToRawLongBits(value)>>62)|1); - } - - /* - * min/max ranges - */ - - /** - * @return True if the specified value is in the specified range (inclusive), false otherwise. - */ - public static boolean isInRange(int min, int max, int a) { - return (min <= a) && (a <= max); - } - - /** - * @return True if the specified value is in the specified range (inclusive), false otherwise. - */ - public static boolean isInRange(long min, long max, long a) { - return (min <= a) && (a <= max); - } - - /** - * Returns false if any value is NaN. - * - * @return True if the specified value is in the specified range (inclusive), false otherwise. - */ - public static boolean isInRange(float min, float max, float a) { - return (min <= a) && (a <= max); - } - - /** - * Returns false if any value is NaN. - * - * @return True if the specified value is in the specified range (inclusive), false otherwise. - */ - public static boolean isInRange(double min, double max, double a) { - return (min <= a) && (a <= max); - } - - /* - * - */ - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive). - */ - public static boolean checkIsInRange(int min, int max, int a) { - if (!isInRange(min, max, a)) { - throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive). - */ - public static boolean checkIsInRange(long min, long max, long a) { - if (!isInRange(min, max, a)) { - throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive) - * or any parameter is NaN. - */ - public static boolean checkIsInRange(float min, float max, float a) { - if (!isInRange(min, max, a)) { - throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive) - * or any parameter is NaN. - */ - public static boolean checkIsInRange(double min, double max, double a) { - if (!isInRange(min, max, a)) { - throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); - } - return true; - } - - /* - * - */ - - /** - * @param min A value. - * @param max A value. - * @param a A value. - * @return min if a <= min, else max if a >= max, else a. - */ - public static int toRange(int min, int max, int a) { - if (a <= min) { - return min; - } else if (a >= max) { - return max; - } else { - return a; - } - } - - /** - * @param min A value. - * @param max A value. - * @param a A value. - * @return min if a <= min, else max if a >= max, else a. - */ - public static long toRange(long min, long max, long a) { - if (a <= min) { - return min; - } else if (a >= max) { - return max; - } else { - return a; - } - } - - /** - * @param min A value. - * @param max A value. - * @param a A value. - * @return min if a <= min, else max if a >= max, else a. - */ - public static float toRange(float min, float max, float a) { - if (a <= min) { - return min; - } else if (a >= max) { - return max; - } else { - return a; - } - } - - /** - * @param min A value. - * @param max A value. - * @param a A value. - * @return min if a <= min, else max if a >= max, else a. - */ - public static double toRange(double min, double max, double a) { - if (a <= min) { - return min; - } else if (a >= max) { - return max; - } else { - return a; - } - } - - /* - * bitwise ranges - */ - - /** - * @param bitSize A number of bits, in [1,32]. - * @return True if the specified value fits as a signed integer - * over the specified number of bits, false otherwise. - * @throws IllegalArgumentException if the specified number of bits is not in [1,32]. - */ - public static boolean isInRangeSigned(int a, int bitSize) { - checkBitSizeForSignedInt(bitSize); - return (minSignedIntForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedIntForBitSize_noCheck(bitSize)); - } - - /** - * @param bitSize A number of bits, in [1,64]. - * @return True if the specified value fits as a signed integer - * over the specified number of bits, false otherwise. - * @throws IllegalArgumentException if the specified number of bits is not in [1,64]. - */ - public static boolean isInRangeSigned(long a, int bitSize) { - checkBitSizeForSignedLong(bitSize); - return (minSignedLongForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedLongForBitSize_noCheck(bitSize)); - } - - /** - * @param bitSize A number of bits, in [1,31]. - * @return True if the specified value fits as an unsigned integer - * over the specified number of bits, false otherwise. - * @throws IllegalArgumentException if the specified number of bits is not in [1,31]. - */ - public static boolean isInRangeUnsigned(int a, int bitSize) { - return isInRange(0, maxUnsignedIntForBitSize(bitSize), a); - } - - /** - * @param bitSize A number of bits, in [1,63]. - * @return True if the specified value fits as an unsigned integer - * over the specified number of bits, false otherwise. - * @throws IllegalArgumentException if the specified number of bits is not in [1,63]. - */ - public static boolean isInRangeUnsigned(long a, int bitSize) { - return isInRange(0, maxUnsignedLongForBitSize(bitSize), a); - } - - /* - * - */ - - /** - * @param bitSize A number of bits, in [1,32]. - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value does not fit - * as a signed integer over the specified number of bits. - */ - public static boolean checkIsInRangeSigned(int a, int bitSize) { - if (!isInRangeSigned(a, bitSize)) { - throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits"); - } - return true; - } - - /** - * @param bitSize A number of bits, in [1,64]. - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value does not fit - * as a signed integer over the specified number of bits. - */ - public static boolean checkIsInRangeSigned(long a, int bitSize) { - if (!isInRangeSigned(a, bitSize)) { - throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits"); - } - return true; - } - - /** - * @param bitSize A number of bits, in [1,31]. - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value does not fit - * as an unsigned integer over the specified number of bits. - */ - public static boolean checkIsInRangeUnsigned(int a, int bitSize) { - if (!isInRangeUnsigned(a, bitSize)) { - throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits"); - } - return true; - } - - /** - * @param bitSize A number of bits, in [1,63]. - * @return True if does not throw. - * @throws IllegalArgumentException if the specified value does not fit - * as an unsigned integer over the specified number of bits. - */ - public static boolean checkIsInRangeUnsigned(long a, int bitSize) { - if (!isInRangeUnsigned(a, bitSize)) { - throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits"); - } - return true; - } - - /* - * masks (int) - */ - - /** - * @param bitSize A number of bits, in [0,32]. - * @return Mask with the specified number of left bits set with 0, - * and other bits set with 1. - */ - public static int intMaskMSBits0(int bitSize) { - checkIsInRange(0, 32, bitSize); - // Shifting in two times, for >>> doesn't work for full bit size (<< as well). - final int halfish = (bitSize>>1); - return ((-1)>>>halfish)>>>(bitSize-halfish); - } - - /** - * @param bitSize A number of bits, in [0,32]. - * @return Mask with the specified number of left bits set with 1, - * and other bits set with 0. - */ - public static int intMaskMSBits1(int bitSize) { - return ~intMaskMSBits0(bitSize); - } - - /** - * @param bitSize A number of bits, in [0,32]. - * @return Mask with the specified number of right bits set with 0, - * and other bits set with 1. - */ - public static int intMaskLSBits0(int bitSize) { - return ~intMaskMSBits0(32-bitSize); - } - - /** - * @param bitSize A number of bits, in [0,32]. - * @return Mask with the specified number of right bits set with 1, - * and other bits set with 0. - */ - public static int intMaskLSBits1(int bitSize) { - return intMaskMSBits0(32-bitSize); - } - - /* - * masks (long) - */ - - /** - * @param bitSize A number of bits, in [0,64]. - * @return Mask with the specified number of left bits set with 0, - * and other bits set with 1. - */ - public static long longMaskMSBits0(int bitSize) { - checkIsInRange(0, 64, bitSize); - // Shifting in two times, for >>> doesn't work for full bit size (<< as well). - final int halfish = (bitSize>>1); - return ((-1L)>>>halfish)>>>(bitSize-halfish); - } - - /** - * @param bitSize A number of bits, in [0,64]. - * @return Mask with the specified number of left bits set with 1, - * and other bits set with 0. - */ - public static long longMaskMSBits1(int bitSize) { - return ~longMaskMSBits0(bitSize); - } - - /** - * @param bitSize A number of bits, in [0,64]. - * @return Mask with the specified number of right bits set with 0, - * and other bits set with 1. - */ - public static long longMaskLSBits0(int bitSize) { - return ~longMaskMSBits0(64-bitSize); - } - - /** - * @param bitSize A number of bits, in [0,64]. - * @return Mask with the specified number of right bits set with 1, - * and other bits set with 0. - */ - public static long longMaskLSBits1(int bitSize) { - return longMaskMSBits0(64-bitSize); - } - - /* - * signed/unsigned - */ - - /** - * @return Unsigned value corresponding to bits of the specified byte. - */ - public static short byteAsUnsigned(byte value) { - return (short)(((short)value) & 0xFF); - } - - /** - * @return Unsigned value corresponding to bits of the specified short. - */ - public static int shortAsUnsigned(short value) { - return ((int)value) & 0xFFFF; - } - - /** - * @return Unsigned value corresponding to bits of the specified int. - */ - public static long intAsUnsigned(int value) { - return ((long)value) & 0xFFFFFFFF; - } - - /* - * bitwise ranges - */ - - /** - * @return True if a signed int value can be read over the specified number of bits, - * i.e. if it is in [1,32], false otherwise. - */ - public static boolean isValidBitSizeForSignedInt(int bitSize) { - return (bitSize > 0) && (bitSize <= 32); - } - - /** - * @return True if a signed long value can be read over the specified number of bits, - * i.e. if it is in [1,64], false otherwise. - */ - public static boolean isValidBitSizeForSignedLong(int bitSize) { - return (bitSize > 0) && (bitSize <= 64); - } - - /** - * @return True if an unsigned int value can be read over the specified number of bits, - * i.e. if it is in [1,31], false otherwise. - */ - public static boolean isValidBitSizeForUnsignedInt(int bitSize) { - return (bitSize > 0) && (bitSize < 32); - } - - /** - * @return True if an unsigned long value can be read over the specified number of bits, - * i.e. if it is in [1,63], false otherwise. - */ - public static boolean isValidBitSizeForUnsignedLong(int bitSize) { - return (bitSize > 0) && (bitSize < 64); - } - - /* - * - */ - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if a signed int value can't be read over the - * specified number of bits, i.e. if it is not in [1,32]. - */ - public static boolean checkBitSizeForSignedInt(int bitSize) { - if (!isValidBitSizeForSignedInt(bitSize)) { - throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,32] for signed int values"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if a signed long value can't be read over the - * specified number of bits, i.e. if it is not in [1,64]. - */ - public static boolean checkBitSizeForSignedLong(int bitSize) { - if (!isValidBitSizeForSignedLong(bitSize)) { - throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,64] for signed long values"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if an unsigned int value can't be read over the - * specified number of bits, i.e. if it is not in [1,31]. - */ - public static boolean checkBitSizeForUnsignedInt(int bitSize) { - if (!isValidBitSizeForUnsignedInt(bitSize)) { - throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,31] for unsigned int values"); - } - return true; - } - - /** - * @return True if does not throw. - * @throws IllegalArgumentException if an unsigned long value can't be read over the - * specified number of bits, i.e. if it is not in [1,63]. - */ - public static boolean checkBitSizeForUnsignedLong(int bitSize) { - if (!isValidBitSizeForUnsignedLong(bitSize)) { - throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,63] for unsigned long values"); - } - return true; - } - - /* - * - */ - - /** - * @param bitSize A number of bits in [1,32]. - * @return The min signed int value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static int minSignedIntForBitSize(int bitSize) { - checkBitSizeForSignedInt(bitSize); - return minSignedIntForBitSize_noCheck(bitSize); - } - - /** - * @param bitSize A number of bits in [1,64]. - * @return The min signed long value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static long minSignedLongForBitSize(int bitSize) { - checkBitSizeForSignedLong(bitSize); - return minSignedLongForBitSize_noCheck(bitSize); - } - - /** - * @param bitSize A number of bits in [1,32]. - * @return The max signed int value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static int maxSignedIntForBitSize(int bitSize) { - checkBitSizeForSignedInt(bitSize); - return maxSignedIntForBitSize_noCheck(bitSize); - } - - /** - * @param bitSize A number of bits in [1,64]. - * @return The max signed long value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static long maxSignedLongForBitSize(int bitSize) { - checkBitSizeForSignedLong(bitSize); - return maxSignedLongForBitSize_noCheck(bitSize); - } - - /** - * @param bitSize A number of bits in [1,31]. - * @return The max unsigned int value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static int maxUnsignedIntForBitSize(int bitSize) { - checkBitSizeForUnsignedInt(bitSize); - // i.e. (1<>(31-bitSize)); - } - - /** - * @param bitSize A number of bits in [1,63]. - * @return The max unsigned long value that can be stored over the specified number of bits. - * @throws IllegalArgumentException if the specified number of bits is out of range. - */ - public static long maxUnsignedLongForBitSize(int bitSize) { - checkBitSizeForUnsignedLong(bitSize); - // i.e. (1L<>(63-bitSize)); - } - - /* - * - */ - - /** - * @return The number of bits required to store the specified value as a signed integer, - * i.e. a result in [1,32]. - */ - public static int bitSizeForSignedValue(int value) { - if (value > 0) { - return 33-Integer.numberOfLeadingZeros(value); - } else if (value == 0) { - return 1; - } else { - // Works for Integer.MIN_VALUE as well. - return 33-Integer.numberOfLeadingZeros(-value-1); - } - } - - /** - * @return The number of bits required to store the specified value as a signed integer, - * i.e. a result in [1,64]. - */ - public static int bitSizeForSignedValue(long value) { - if (value > 0) { - return 65-Long.numberOfLeadingZeros(value); - } else if (value == 0) { - return 1; - } else { - // Works for Long.MIN_VALUE as well. - return 65-Long.numberOfLeadingZeros(-value-1); - } - } - - /** - * @param value An integer value in [0,Integer.MAX_VALUE]. - * @return The number of bits required to store the specified value as an unsigned integer, - * i.e. a result in [1,31]. - * @throws IllegalArgumentException if the specified value is < 0. - */ - public static int bitSizeForUnsignedValue(int value) { - if (value > 0) { - return 32-Integer.numberOfLeadingZeros(value); - } else { - if (value == 0) { - return 1; - } else { - throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0"); - } - } - } - - /** - * @param value An integer value in [0,Long.MAX_VALUE]. - * @return The number of bits required to store the specified value as an unsigned integer, - * i.e. a result in [1,63]. - * @throws IllegalArgumentException if the specified value is < 0. - */ - public static int bitSizeForUnsignedValue(long value) { - if (value > 0) { - return 64-Long.numberOfLeadingZeros(value); - } else { - if (value == 0) { - return 1; - } else { - throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0"); - } - } - } - - /* - * integer functions - */ - - /** - * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise. - */ - public static int signum(int a) { - return (a < 0) ? -1 : ((a == 0) ? 0 : 1); - } - - /** - * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise. - */ - public static int signum(long a) { - return (a < 0) ? -1 : ((a == 0) ? 0 : 1); - } - - /** - * @return True if the specified value is even, false otherwise. - */ - public static boolean isEven(int a) { - return ((a&1) == 0); - } - - /** - * @return True if the specified value is even, false otherwise. - */ - public static boolean isEven(long a) { - // faster to work on ints - return isEven((int)a); - } - - /** - * @return True if the specified value is odd, false otherwise. - */ - public static boolean isOdd(int a) { - return ((a&1) != 0); - } - - /** - * @return True if the specified value is odd, false otherwise. - */ - public static boolean isOdd(long a) { - // faster to work on ints - return isOdd((int)a); - } - - /** - * @return True if the specified values are both even or both odd, false otherwise. - */ - public static boolean haveSameEvenness(int a, int b) { - return (((a^b)&1) == 0); - } - - /** - * @return True if the specified values are both even or both odd, false otherwise. - */ - public static boolean haveSameEvenness(long a, long b) { - // faster to work on ints - return haveSameEvenness((int)a, (int)b); - } - - /** - * @return True if the specified values are both >= 0 or both < 0, false otherwise. - */ - public static boolean haveSameSign(int a, int b) { - return ((a^b) >= 0); - } - - /** - * @return True if the specified values are both >= 0 or both < 0, false otherwise. - */ - public static boolean haveSameSign(long a, long b) { - return ((a^b) >= 0); - } - - /** - * @return True if the specified value is a power of two, - * i.e. a value of the form 2^k, with k >= 0. - */ - public static boolean isPowerOfTwo(int a) { - if (a <= 0) { - return false; - } - if (false) { - // also works - return (a & -a) == a; - } - return (a & (a-1)) == 0; - } - - /** - * @return True if the specified value is a power of two, - * i.e. a value of the form 2^k, with k >= 0. - */ - public static boolean isPowerOfTwo(long a) { - if (a <= 0) { - return false; - } - if (false) { - // also works - return (a & -a) == a; - } - return (a & (a-1)) == 0; - } - - /** - * @return True if the specified value is a signed power of two, - * i.e. a value of the form +-2^k, with k >= 0. - */ - public static boolean isSignedPowerOfTwo(int a) { - if (a > 0) { - return (a & (a-1)) == 0; - } else { - if (a == -a) { - // a is 0 or Integer.MIN_VALUE - return (a != 0); - } - return ((-a) & (-a-1)) == 0; - } - } - - /** - * @return True if the specified value is a signed power of two, - * i.e. a value of the form +-2^k, with k >= 0. - */ - public static boolean isSignedPowerOfTwo(long a) { - if (a > 0) { - return (a & (a-1)) == 0; - } else { - if (a == -a) { - // a is 0 or Long.MIN_VALUE - return (a != 0); - } - return ((-a) & (-a-1)) == 0; - } - } - - /** - * @param a A value in [1,Integer.MAX_VALUE]. - * @return The highest power of two <= a. - */ - public static int floorPowerOfTwo(int a) { - if (a <= 0) { - throw new IllegalArgumentException("a ["+a+"] must be > 0"); - } - return Integer.highestOneBit(a); - } - - /** - * @param a A value in [1,Long.MAX_VALUE]. - * @return The highest power of two <= a. - */ - public static long floorPowerOfTwo(long a) { - if (a <= 0) { - throw new IllegalArgumentException("a ["+a+"] must be > 0"); - } - // Faster than copying int method - // (less computations on long). - return 1L << (63 - Long.numberOfLeadingZeros(a)); - } - - /** - * @param a A value in [0,2^30]. - * @return The lowest power of two >= a. - */ - public static int ceilingPowerOfTwo(int a) { - checkIsInRange(0, (1<<30), a); - return (a >= 2) ? Integer.highestOneBit((a-1)<<1) : 1; - } - - /** - * @param a A value in [0,2^62]. - * @return The lowest power of two >= a. - */ - public static long ceilingPowerOfTwo(long a) { - checkIsInRange(0L, (1L<<62), a); - // Faster than copying int method - // (less computations on long). - return 1L << (64 - Long.numberOfLeadingZeros(a - 1)); - } - - /** - * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division). - */ - public static int meanLow(int a, int b) { - return (a & b) + ((a ^ b) >> 1); - } - - /** - * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division). - */ - public static long meanLow(long a, long b) { - return (a & b) + ((a ^ b) >> 1); - } - - /** - * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division). - */ - public static int meanSml(int a, int b) { - int result = meanLow(a,b); - if (!haveSameEvenness(a, b)) { - // inexact - if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) { - // both < 0, or only one is < 0 and it has the largest magnitude - result++; - } - } - return result; - } - - /** - * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division). - */ - public static long meanSml(long a, long b) { - long result = meanLow(a,b); - if (!haveSameEvenness(a, b)) { - // inexact - if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) { - // both < 0, or only one is < 0 and it has the largest magnitude - result++; - } - } - return result; - } - - /** - * Useful because a positive int value could not represent half the width - * of full int range width, which is mathematically Integer.MAX_VALUE+1. - * - * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude) - * between the specified bounds. - * @throws IllegalArgumentException if min > max. - */ - public static int negHalfWidth(int min, int max) { - if (min > max) { - throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]"); - } - int mean = meanLow(min, max); - return min - mean - ((min^max)&1); - } - - /** - * Useful because a positive long value could not represent half the width - * of full long range width, which is mathematically Long.MAX_VALUE+1. - * - * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude) - * between the specified bounds. - * @throws IllegalArgumentException if min > max. - */ - public static long negHalfWidth(long min, long max) { - if (min > max) { - throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]"); - } - long mean = meanLow(min, max); - return min - mean - ((min^max)&1); - } - - /** - * This treatment being designed for optimization, the fact that spot - * is a signed power of two is not checked. - * - * @param value A value. - * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0). - * @return value % spot, i.e. a value in ]-|spot|,|spot|[. - */ - public static int moduloSignedPowerOfTwo(int value, int spot) { - if (spot == Integer.MIN_VALUE) { - return (value != Integer.MIN_VALUE) ? value : 0; - } else { - int s = (value>>31); - return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s; - } - } - - /** - * This treatment being designed for optimization, the fact that spot - * is a signed power of two is not checked. - * - * @param value A value. - * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0). - * @return value % spot, i.e. a value in ]-|spot|,|spot|[. - */ - public static long moduloSignedPowerOfTwo(long value, long spot) { - if (spot == Long.MIN_VALUE) { - return (value != Long.MIN_VALUE) ? value : 0; - } else { - long s = (value>>63); - return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s; - } - } - - /** - * @param value An integer value > 0. - * @return The integer part of the logarithm, in base 2, of the specified value, - * i.e. a result in [0,30] - * @throws IllegalArgumentException if the specified value is <= 0. - */ - public static int log2(int value) { - if (value <= 0) { - throw new IllegalArgumentException("value ["+value+"] must be > 0"); - } - return 31-Integer.numberOfLeadingZeros(value); - } - - /** - * @param value An integer value > 0. - * @return The integer part of the logarithm, in base 2, of the specified value, - * i.e. a result in [0,62] - * @throws IllegalArgumentException if the specified value is <= 0. - */ - public static int log2(long value) { - if (value <= 0) { - throw new IllegalArgumentException("value ["+value+"] must be > 0"); - } - return 63-Long.numberOfLeadingZeros(value); - } - - /** - * Possibly faster than java.lang.Math.abs(int). - * - * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE. - */ - public static int abs(int a) { - return (a^(a>>31))-(a>>31); - } - - /** - * Possibly faster than java.lang.Math.abs(long). - * - * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE. - */ - public static long abs(long a) { - return (a^(a>>63))-(a>>63); - } - - /** - * @return The negative of the absolute value (always exact). - */ - public static int absNeg(int a) { - return (a>>31)-(a^(a>>31)); - } - - /** - * @return The negative of the absolute value (always exact). - */ - public static long absNeg(long a) { - return (a>>63)-(a^(a>>63)); - } - - /** - * If the specified value is in int range, the returned value is identical. - * - * @return An int hash of the specified value. - */ - public static int intHash(long a) { - if (false) { - // also works - int hash = ((int)(a>>32)) ^ ((int)a); - if (a < 0) { - hash = -hash-1; - } - return hash; - } - int hash = ((int)(a>>32)) + ((int)a); - if (a < 0) { - hash++; - } - return hash; - } - - /** - * Not defining an asByte(long) method, since asByte((int)aLong) works. - * - * @param a An int value. - * @return The specified value as byte. - * @throws ArithmeticException if the specified value is not in [Byte.MIN_VALUE,Byte.MAX_VALUE] range. - */ - public static byte asByte(int a) { - if (a != (byte)a) { - throw new ArithmeticException("overflow: "+a); - } - return (byte)a; - } - - /** - * @param a A long value. - * @return The specified value as int. - * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int asInt(long a) { - if (a != (int)a) { - throw new ArithmeticException("overflow: "+a); - } - return (int)a; - } - - /** - * @param a A long value. - * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int toInt(long a) { - if (a != (int)a) { - return (a < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; - } - return (int)a; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a+b. - * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int plusExact(int a, int b) { - final int sum = a + b; - // HD 2-12 Overflow iff both arguments - // have the opposite sign of the result. - if (((a ^ sum) & (b ^ sum)) < 0) { - throw new ArithmeticException("overflow: "+a+"+"+b); - } - return sum; - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a+b. - * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long plusExact(long a, long b) { - final long sum = a + b; - // HD 2-12 Overflow iff both arguments - // have the opposite sign of the result. - if (((a ^ sum) & (b ^ sum)) < 0) { - throw new ArithmeticException("overflow: "+a+"+"+b); - } - return sum; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b. - */ - public static int plusBounded(int a, int b) { - return toInt(((long)a) + ((long)b)); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b. - */ - public static long plusBounded(long a, long b) { - final long sum = a + b; - if (((a ^ sum) & (b ^ sum)) < 0) { - return (sum >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE; - } - return sum; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a-b. - * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int minusExact(int a, int b) { - final int diff = a - b; - // HD 2-12 Overflow iff the arguments have different signs and - // the sign of the result is different than the sign of "a". - if (((a ^ b) & (a ^ diff)) < 0) { - throw new ArithmeticException("integer overflow"); - } - return diff; - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a-b. - * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long minusExact(long a, long b) { - final long diff = a - b; - // HD 2-12 Overflow iff the arguments have different signs and - // the sign of the result is different than the sign of "a". - if (((a ^ b) & (a ^ diff)) < 0) { - throw new ArithmeticException("integer overflow"); - } - return diff; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b. - */ - public static int minusBounded(int a, int b) { - return toInt(((long)a) - ((long)b)); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b. - */ - public static long minusBounded(long a, long b) { - final long diff = a - b; - if (((a ^ b) & (a ^ diff)) < 0) { - return (diff >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE; - } - return diff; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The mathematical result of a*b. - * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. - */ - public static int timesExact(int a, int b) { - final long prod = a * (long)b; - if (prod != (int)prod) { - throw new ArithmeticException("overflow: "+a+"*"+b); - } - return (int)prod; - } - - /** - * @param a A long value. - * @param b A long value. - * @return The mathematical result of a*b. - * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. - */ - public static long timesExact(long a, long b) { - final long prod = a * b; - final long absA = abs(a); - final long absB = abs(b); - if (((absA|absB)>>>31) != 0) { - // Some bits greater than 2^31 that might cause overflow - // Check the result using the divide operator - // and check for the special case of Long.MIN_VALUE * -1 - if (((b != 0) && (prod/b != a)) || - ((a == Long.MIN_VALUE) && (b == -1))) { - throw new ArithmeticException("overflow: "+a+"*"+b); - } - } - return prod; - } - - /** - * @param a An int value. - * @param b An int value. - * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b. - */ - public static int timesBounded(int a, int b) { - return (int)(a * (double)b); - } - - /** - * @param a A long value. - * @param b A long value. - * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. - */ - public static long timesBounded(long a, long b) { - final long prod = a * b; - final long absA = abs(a); - final long absB = abs(b); - if (((absA|absB)>>>31) != 0) { - // Some bits greater than 2^31 that might cause overflow - // Check the result using the divide operator - // and check for the special case of Long.MIN_VALUE * -1 - if (((b != 0) && (prod/b != a)) || - ((a == Long.MIN_VALUE) && (b == -1))) { - return ((a^b) >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE; - } - } - return prod; - } - - /* - * powers - */ - - /** - * Returns the exact result, provided it's in double range, - * i.e. if power is in [-1074,1023]. - * - * @param power An int power. - * @return 2^power as a double, or +-Infinity in case of overflow. - */ - public static double twoPow(int power) { - if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal. - if (power >= MIN_DOUBLE_EXPONENT) { // Subnormal. - return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT))); - } else { // Underflow. - return 0.0; - } - } else if (power > MAX_DOUBLE_EXPONENT) { // Overflow. - return Double.POSITIVE_INFINITY; - } else { // Normal. - return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); - } - } - - /** - * @param power An int power. - * @return 2^power as an int. - * @throws ArithmeticException if the mathematical result - * is not in int range, i.e. if power is not in [0,30]. - */ - public static int twoPowAsIntExact(int power) { - if ((power < 0) || (power > 30)) { - throw new ArithmeticException("integer overflow"); - } - return 1 << power; - } - - /** - * @param power An int power. - * @return 2^power as an int, or the closest power of two in int range - * in case of overflow, i.e. if power is not in [0,30]. - */ - public static int twoPowAsIntBounded(int power) { - power = toRange(0, 30, power); - return 1 << power; - } - - /** - * @param power An int power. - * @return 2^power as a long. - * @throws ArithmeticException if the mathematical result - * is not in long range, i.e. if power is not in [0,62]. - */ - public static long twoPowAsLongExact(int power) { - if ((power < 0) || (power > 62)) { - throw new ArithmeticException("long overflow"); - } - return 1L << power; - } - - /** - * @param power An int power. - * @return 2^power as a long, or the closest power of two in long range - * in case of overflow, i.e. if power is not in [0,62]. - */ - public static long twoPowAsLongBounded(int power) { - power = toRange(0, 62, power); - return 1L << power; - } - - /** - * @param a A value. - * @return a*a. - */ - public static int pow2(int a) { - return a*a; - } - - /** - * @param a A value. - * @return a*a. - */ - public static long pow2(long a) { - return a*a; - } - - /** - * @param a A value. - * @return a*a. - */ - public static float pow2(float a) { - return a*a; - } - - /** - * Strict version. - * - * @param a A value. - * @return a*a. - */ - public static strictfp float pow2_strict(float a) { - return a*a; - } - - /** - * @param a A value. - * @return a*a. - */ - public static double pow2(double a) { - return a*a; - } - - /** - * Strict version. - * - * @param a A value. - * @return a*a. - */ - public static strictfp double pow2_strict(double a) { - return a*a; - } - - /** - * @param a A value. - * @return a*a*a. - */ - public static int pow3(int a) { - return a*a*a; - } - - /** - * @param a A value. - * @return a*a*a. - */ - public static long pow3(long a) { - return a*a*a; - } - - /** - * @param a A value. - * @return a*a*a. - */ - public static float pow3(float a) { - return a*a*a; - } - - /** - * Strict version. - * - * @param a A value. - * @return a*a*a. - */ - public static strictfp float pow3_strict(float a) { - return a*a*a; - } - - /** - * @param a A value. - * @return a*a*a. - */ - public static double pow3(double a) { - return a*a*a; - } - - /** - * Strict version. - * - * @param a A value. - * @return a*a*a. - */ - public static strictfp double pow3_strict(double a) { - return a*a*a; - } - - /* - * Accurate +-m*PI/n. - */ - - /** - * @param angRad An angle, in radians. - * @return angRad + 2*PI, accurately computed. - */ - public static double plus2PI(double angRad) { - if (angRad > -Math.PI) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + TWOPI_LO) + TWOPI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + TWOPI_HI) + TWOPI_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad + 2*PI, accurately computed. - */ - public static strictfp double plus2PI_strict(double angRad) { - if (angRad > -Math.PI) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + TWOPI_LO) + TWOPI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + TWOPI_HI) + TWOPI_LO; - } - } - - /** - * @param angRad An angle, in radians. - * @return angRad - 2*PI, accurately computed. - */ - public static double minus2PI(double angRad) { - if (angRad < Math.PI) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - TWOPI_LO) - TWOPI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - TWOPI_HI) - TWOPI_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad - 2*PI, accurately computed. - */ - public static strictfp double minus2PI_strict(double angRad) { - if (angRad < Math.PI) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - TWOPI_LO) - TWOPI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - TWOPI_HI) - TWOPI_LO; - } - } - - /** - * @param angRad An angle, in radians. - * @return angRad + PI, accurately computed. - */ - public static double plusPI(double angRad) { - if (angRad > -Math.PI/2) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + PI_LO) + PI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + PI_HI) + PI_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad + PI, accurately computed. - */ - public static strictfp double plusPI_strict(double angRad) { - if (angRad > -Math.PI/2) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + PI_LO) + PI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + PI_HI) + PI_LO; - } - } - - /** - * @param angRad An angle, in radians. - * @return angRad - PI, accurately computed. - */ - public static double minusPI(double angRad) { - if (angRad < Math.PI/2) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - PI_LO) - PI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - PI_HI) - PI_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad - PI, accurately computed. - */ - public static strictfp double minusPI_strict(double angRad) { - if (angRad < Math.PI/2) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - PI_LO) - PI_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - PI_HI) - PI_LO; - } - } - - /** - * @param angRad An angle, in radians. - * @return angRad + PI/2, accurately computed. - */ - public static double plusPIO2(double angRad) { - if (angRad > -Math.PI/4) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + PIO2_LO) + PIO2_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + PIO2_HI) + PIO2_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad + PI/2, accurately computed. - */ - public static strictfp double plusPIO2_strict(double angRad) { - if (angRad > -Math.PI/4) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad + PIO2_LO) + PIO2_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad + PIO2_HI) + PIO2_LO; - } - } - - /** - * @param angRad An angle, in radians. - * @return angRad - PI/2, accurately computed. - */ - public static double minusPIO2(double angRad) { - if (angRad < Math.PI/4) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - PIO2_LO) - PIO2_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - PIO2_HI) - PIO2_LO; - } - } - - /** - * Strict version. - * - * @param angRad An angle, in radians. - * @return angRad - PI/2, accurately computed. - */ - public static strictfp double minusPIO2_strict(double angRad) { - if (angRad < Math.PI/4) { - // LO then HI, for better accuracy (if starting near 0). - return (angRad - PIO2_LO) - PIO2_HI; - } else { - // HI then LO, for better accuracy (if ending near 0). - return (angRad - PIO2_HI) - PIO2_LO; - } - } - - /* - * toString (radix) - */ - - /** - * @param radix Radix to be checked. - * @return True if does not throw. - * @throws IllegalArgumentException if the specified radix is not in [2,36]. - */ - public static boolean checkRadix(int radix) { - if (!isInRange(Character.MIN_RADIX, Character.MAX_RADIX, radix)) { - throw new IllegalArgumentException("radix ["+radix+"] must be in ["+Character.MIN_RADIX+","+Character.MAX_RADIX+"]"); - } - return true; - } - - /** - * @param radix A radix in [2,36]. - * @return Number of characters (minus sign included) - * to represent the specified value in the specified radix. - */ - public static int computeNbrOfChars(int value, int radix) { - if (value < 0) { - // 1 for sign - return 1 + computeNbrOfDigits_negValue(value, radix); - } else { - return computeNbrOfDigits_negValue(-value, radix); - } - } - - /** - * @param radix A radix in [2,36]. - * @return Number of characters (minus sign included) - * to represent the specified value in the specified radix. - */ - public static int computeNbrOfChars(long value, int radix) { - if (value < 0) { - // 1 for sign - return 1 + computeNbrOfDigits_negValue(value, radix); - } else { - return computeNbrOfDigits_negValue(-value, radix); - } - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return Number of characters (minus sign included) - * to represent the specified value in the specified radix. - */ - public static int computeNbrOfChars(int value, int radix, int paddingUpTo) { - if (value < 0) { - // 1 for sign - return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix)); - } else { - return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix)); - } - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return Number of characters (minus sign included) - * to represent the specified value in the specified radix. - */ - public static int computeNbrOfChars(long value, int radix, int paddingUpTo) { - if (value < 0) { - // 1 for sign - return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix)); - } else { - return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix)); - } - } - - /** - * @param radix A radix in [2,36]. - * @return Number of digits of the specified value in the specified radix. - */ - public static int computeNbrOfDigits(int value, int radix) { - return computeNbrOfDigits_negValue(-abs(value), radix); - } - - /** - * @param radix A radix in [2,36]. - * @return Number of digits of the specified value in the specified radix. - */ - public static int computeNbrOfDigits(long value, int radix) { - return computeNbrOfDigits_negValue(-abs(value), radix); - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return Number of digits of the specified value in the specified radix, - * including the specified padding. - */ - public static int computeNbrOfDigits(int value, int radix, int paddingUpTo) { - return Math.max(paddingUpTo,computeNbrOfDigits(value, radix)); - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return Number of digits of the specified value in the specified radix, - * including the specified padding. - */ - public static int computeNbrOfDigits(long value, int radix, int paddingUpTo) { - return Math.max(paddingUpTo,computeNbrOfDigits(value, radix)); - } - - /** - * This method just delegates to Integer.toString(int), - * but is defined here to complete the API. - * - * @return String representation of the specified value in base 10. - */ - public static String toString(int value) { - return Integer.toString(value); - } - - /** - * This method just delegates to Long.toString(long), - * but is defined here to complete the API. - * - * @return String representation of the specified value in base 10. - */ - public static String toString(long value) { - return Long.toString(value); - } - - /** - * @param radix A radix in [2,36]. - * @return String representation of the specified value in the specified radix. - * @throws IllegalArgumentException if the specified radix is out of range. - */ - public static String toString(int value, int radix) { - return toString(value, radix, 0); - } - - /** - * @param radix A radix in [2,36]. - * @return String representation of the specified value in the specified radix. - * @throws IllegalArgumentException if the specified radix is out of range. - */ - public static String toString(long value, int radix) { - return toString(value, radix, 0); - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return String representation of the specified value in the specified radix. - * @throws IllegalArgumentException if the specified radix is out of range. - */ - public static String toString(int value, int radix, int paddingUpTo) { - // Only one test if radix+paddingUpTo != 10. - if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) { - // Using JDK's optimized algorithm. - return Integer.toString(value); - } - - int negValue; - final int signSize; - final boolean negative = (value < 0); - if (negative) { - negValue = value; - signSize = 1; - } else { - negValue = -value; - signSize = 0; - } - // Faster if we just use max possible number of characters (33), - // but we prefer to take care of garbage's memory footprint. - // Checks radix. - final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix)); - - final char[] chars = new char[nbrOfChars]; - - int charPos = nbrOfChars; - - final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0); - // Not allowing Integer.MIN_VALUE so it can be negated. - if (radixIsPowerOfTwo && (negValue != Integer.MIN_VALUE)) { - final int mask = radix-1; - final int divShift = DIV_SHIFT_BY_RADIX[radix]; - while (negValue <= -radix) { - chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)]; - negValue = -((-negValue) >> divShift); - } - } else { - while (negValue <= -radix) { - chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))]; - negValue /= radix; - } - } - chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)]; - - while (charPos > signSize) { - chars[--charPos] = '0'; - } - - if (negative) { - chars[0] = '-'; - } - - return new String(chars); - } - - /** - * @param radix A radix in [2,36]. - * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. - * @return String representation of the specified value in the specified radix. - * @throws IllegalArgumentException if the specified radix is out of range. - */ - public static String toString(long value, int radix, int paddingUpTo) { - // Only one test if radix+paddingUpTo != 10. - if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) { - // Using JDK's optimized algorithm. - return Long.toString(value); - } - - long negValue; - final int signSize; - final boolean negative = (value < 0); - if (negative) { - negValue = value; - signSize = 1; - } else { - negValue = -value; - signSize = 0; - } - // Checks radix. - final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix)); - - final char[] chars = new char[nbrOfChars]; - - int charPos = nbrOfChars; - - final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0); - // Not allowing Long.MIN_VALUE so it can be negated. - if (radixIsPowerOfTwo && (negValue != Long.MIN_VALUE)) { - final int mask = radix-1; - final int divShift = DIV_SHIFT_BY_RADIX[radix]; - while (negValue <= -radix) { - chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)]; - negValue = -((-negValue) >> divShift); - } - } else { - while (negValue <= -radix) { - chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))]; - negValue /= radix; - } - } - chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)]; - - while (charPos > signSize) { - chars[--charPos] = '0'; - } - - if (negative) { - chars[0] = '-'; - } - - return new String(chars); - } - - /* - * toString (bits) - */ - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @return True if does not throw. - * @throws IllegalArgumentException if the specified bit range does not fit in a byte. - */ - public static boolean checkBitPositionsByte(int firstBitPos, int lastBitPosExcl) { - return checkBitPositions(firstBitPos, lastBitPosExcl, 8); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @return True if does not throw. - * @throws IllegalArgumentException if the specified bit range does not fit in a short. - */ - public static boolean checkBitPositionsShort(int firstBitPos, int lastBitPosExcl) { - return checkBitPositions(firstBitPos, lastBitPosExcl, 16); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @return True if does not throw. - * @throws IllegalArgumentException if the specified bit range does not fit in an int. - */ - public static boolean checkBitPositionsInt(int firstBitPos, int lastBitPosExcl) { - return checkBitPositions(firstBitPos, lastBitPosExcl, 32); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @return True if does not throw. - * @throws IllegalArgumentException if the specified bit range does not fit in a long. - */ - public static boolean checkBitPositionsLong(int firstBitPos, int lastBitPosExcl) { - return checkBitPositions(firstBitPos, lastBitPosExcl, 64); - } - - /** - * @return String representation of specified bits, in big endian. - */ - public static String toStringBits(byte bits) { - final char[] chars = new char[8]; - int bitIndex = 8; - while (--bitIndex >= 0) { - chars[7-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - } - return new String(chars); - } - - /** - * @return String representation of specified bits, in big endian. - */ - public static String toStringBits(short bits) { - final char[] chars = new char[16]; - int bitIndex = 16; - while (--bitIndex >= 0) { - chars[15-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - } - return new String(chars); - } - - /** - * @return String representation of specified bits, in big endian. - */ - public static String toStringBits(int bits) { - final char[] chars = new char[32]; - int bitIndex = 32; - while (--bitIndex >= 0) { - chars[31-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - } - return new String(chars); - } - - /** - * @return String representation of specified bits, in big endian. - */ - public static String toStringBits(long bits) { - final char[] chars = new char[64]; - int bitIndex = 64; - while (--bitIndex >= 0) { - chars[63-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - } - return new String(chars); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) - * false for little endian order. - * @param padding True if underscores must be added instead of out-of-range bits, - * false to just add characters corresponding to in-range bits. - * @return String representation of specified bits. - */ - public static String toStringBits( - byte bits, - int firstBitPos, - int lastBitPosExcl, - boolean bigEndian, - boolean padding) { - checkBitPositionsByte(firstBitPos, lastBitPosExcl); - return toStringBits_0_32_bitPosAlreadyChecked(8,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) - * false for little endian order. - * @param padding True if underscores must be added instead of out-of-range bits, - * false to just add characters corresponding to in-range bits. - * @return String representation of specified bits. - */ - public static String toStringBits( - short bits, - int firstBitPos, - int lastBitPosExcl, - boolean bigEndian, - boolean padding) { - checkBitPositionsShort(firstBitPos, lastBitPosExcl); - return toStringBits_0_32_bitPosAlreadyChecked(16,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) - * false for little endian order. - * @param padding True if underscores must be added instead of out-of-range bits, - * false to just add characters corresponding to in-range bits. - * @return String representation of specified bits. - */ - public static String toStringBits( - int bits, - int firstBitPos, - int lastBitPosExcl, - boolean bigEndian, - boolean padding) { - checkBitPositionsInt(firstBitPos, lastBitPosExcl); - return toStringBits_0_32_bitPosAlreadyChecked(32,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); - } - - /** - * @param firstBitPos First bit position (inclusive). - * @param lastBitPosExcl Last bit position (exclusive). - * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) - * false for little endian order. - * @param padding True if underscores must be added instead of out-of-range bits, - * false to just add characters corresponding to in-range bits. - * @return String representation of specified bits. - */ - public static String toStringBits( - long bits, - int firstBitPos, - int lastBitPosExcl, - boolean bigEndian, - boolean padding) { - checkBitPositionsLong(firstBitPos, lastBitPosExcl); - final int bitSize = 64; - final int bitSizeM1 = bitSize-1; - final int lastBitPos = lastBitPosExcl-1; - if (padding) { - final int nbrOfChars = bitSize; - final char[] chars = new char[nbrOfChars]; - int bitIndex = bitSizeM1; - if (bigEndian) { - final int firstBitIndex = bitSizeM1-lastBitPos; - final int lastBitIndex = bitSizeM1-firstBitPos; - while (bitIndex > lastBitIndex) { - chars[bitSizeM1-bitIndex] = '_'; - --bitIndex; - } - while (bitIndex >= firstBitIndex) { - chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - while (bitIndex >= 0) { - chars[bitSizeM1-bitIndex] = '_'; - --bitIndex; - } - } else { - while (bitIndex > lastBitPos) { - chars[bitIndex] = '_'; - --bitIndex; - } - while (bitIndex >= firstBitPos) { - chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - while (bitIndex >= 0) { - chars[bitIndex] = '_'; - --bitIndex; - } - } - return new String(chars); - } else { - final int nbrOfChars = (lastBitPosExcl - firstBitPos); - final char[] chars = new char[nbrOfChars]; - if (bigEndian) { - final int firstBitIndex = bitSizeM1-lastBitPos; - final int lastBitIndex = bitSizeM1-firstBitPos; - int bitIndex = lastBitIndex; - while (bitIndex >= firstBitIndex) { - chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - } else { - int bitIndex = lastBitPos; - while (bitIndex >= firstBitPos) { - chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - } - return new String(chars); - } - } - - /* - * toString (floating points) - * - * toStringCSN(double) and toStringNoCSN(double) - * could be made faster, by using directly internals - * of Double.toString(double), but this would require - * copy-paste of much tricky code from JDK, and - * the overhead of our little rework is relatively - * negligible. - */ - - /** - * @param value A double value. - * @return String representing the specified value, - * using "computerized scientific notation", - * which Double.toString(double) uses for non-infinite - * values, when |value| < 1e-3 or |value| >= 1e7. - */ - public static String toStringCSN(double value) { - // Quick case (also to get rid of +-0.0, - // for which Double.toString(double) doesn't use CSN). - if (value == 0.0) { - if (Double.doubleToRawLongBits(value) < 0) { - return "-0.0E0"; - } else { - return "0.0E0"; - } - } - - final double abs = Math.abs(value); - if ((abs >= NO_CSN_MIN_BOUND_INCL) && (abs < NO_CSN_MAX_BOUND_EXCL)) { - final boolean neg = (value < 0.0); - - final String rawAbs = Double.toString(abs); - if (abs >= 1.0) { - /* - * 0123456 - * 12.3456 ===> 1.23456E1 - * 123.0 ===> 1.23E2 - */ - final int dotIndex = rawAbs.indexOf((int)'.'); - final int powerOfTen = dotIndex-1; - final StringBuilder sb = new StringBuilder(); - if (neg) { - sb.append('-'); - } - // Adding unit-or-above digits, with dot after first one. - sb.append(rawAbs.charAt(0)); - sb.append('.'); - sb.append(rawAbs,1,dotIndex); - if ((value != (int)value) || (abs < 10.0)) { - // Adding below-unit digits (possibly just 0 if abs < 10.0, - // to end up for example with "3.0E0" instead of "3.E0"). - sb.append(rawAbs,dotIndex+1,rawAbs.length()); - } - sb.append('E'); - sb.append(CHAR_BY_DIGIT[powerOfTen]); - return sb.toString(); - } else { - /* - * 012345678 - * 0.0123456 ===> 1.23456E-2 - * 0.01 ===> 1.0E-2 - */ - int nonZeroIndex = 1; - while (rawAbs.charAt(++nonZeroIndex) == '0') { - } - // Negative. - final int powerOfTen = 1-nonZeroIndex; - final int nbrOfSignificantDigitsPastDot = (rawAbs.length() - (nonZeroIndex+1)); - final StringBuilder sb = new StringBuilder(); - if (neg) { - sb.append('-'); - } - sb.append(rawAbs.charAt(nonZeroIndex)); - sb.append('.'); - if (nbrOfSignificantDigitsPastDot > 0) { - // If bug 4428022 make rawAbs being something like "0.0010", - // we add the last '0' here after the dot, which is fine. - sb.append(rawAbs,nonZeroIndex+1,rawAbs.length()); - } else { - sb.append('0'); - } - sb.append("E-"); - sb.append(CHAR_BY_DIGIT[-powerOfTen]); - return sb.toString(); - } - } else { - return Double.toString(value); - } - } - - /** - * @param value A double value. - * @return String representing the specified value, - * not in "computerized scientific notation", - * which Double.toString(double) uses for non-infinite - * values, when |value| < 1e-3 or |value| >= 1e7. - */ - public static String toStringNoCSN(double value) { - // Quick case. - // Should also work with long instead of int, - // but less obvious (due to roundings...), - // and we just want to speed up the more common - // case of "small" integer values. - final int intValue = (int)value; - if (value == intValue) { - if (value == 0.0) { - if (Double.doubleToRawLongBits(value) < 0) { - return "-0.0"; - } else { - return "0.0"; - } - } else { - return Integer.toString(intValue)+".0"; - } - } - - final String raw = Double.toString(value); - final double abs = Math.abs(value); - if (abs >= NO_CSN_MAX_BOUND_EXCL) { - if (abs == Double.POSITIVE_INFINITY) { - return raw; - } - /* - * 0123456789 - * 1.234567E5 ===> 123456.7 - * 1.23456E5 ===> 123456.0 (adding 0) - * 1.23E5 ===> 123000.0 - * 1.0E5 ===> 100000.0 - */ - // "." close to start, so using indexOf. - final int dotIndex = raw.indexOf((int)'.'); - // "E" close to end, so using lastIndexOf. - final int eIndex = raw.lastIndexOf((int)'E'); - final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1)); - final int nbrOfSignificantLoDigits = (eIndex - dotIndex - 1); - final int nbrOfZerosToAddBeforeDot = (powerOfTen - nbrOfSignificantLoDigits); - - int start; - int end; - - final StringBuilder sb = new StringBuilder(); - sb.append(raw,0,dotIndex); - if (nbrOfZerosToAddBeforeDot >= 0) { - // Can copy all digits that were between '.' and 'E'. - sb.append(raw,dotIndex+1,eIndex); - for (int i=0;i 0.0001234 - * 1.0E-4 ===> 0.0001 - */ - // "." close to start, so using indexOf. - final int dotIndex = raw.indexOf((int)'.'); - // "E" close to end, so using lastIndexOf. - final int eIndex = raw.lastIndexOf((int)'E'); - // Negative. - final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1)); - final int nbrOfZerosToAddAfterDot = (-powerOfTen-1); - - final StringBuilder sb = new StringBuilder(); - if (value < 0.0) { - sb.append("-0."); - } else { - sb.append("0."); - } - for (int i=0;i>(32-bitSize)); - } - - private static long minSignedLongForBitSize_noCheck(int bitSize) { - // i.e. (-1L<<(bitSize-1)) - return (Long.MIN_VALUE>>(64-bitSize)); - } - - private static int maxSignedIntForBitSize_noCheck(int bitSize) { - // i.e. (1<<(bitSize-1))-1 - return (Integer.MAX_VALUE>>(32-bitSize)); - } - - private static long maxSignedLongForBitSize_noCheck(int bitSize) { - // i.e. (1L<<(bitSize-1))-1 - return (Long.MAX_VALUE>>(64-bitSize)); - } - - /* - * - */ - - /** - * @throws IllegalArgumentException if the specified radix is out of range. - */ - private static int computeNbrOfDigits_negValue(int negValue, int radix) { - checkRadix(radix); - final int maxNbrOfDigits = MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix]; - int p = radix; - for (int i=1;i -p) { - return i; - } - p *= radix; - } - return maxNbrOfDigits; - } - - /** - * @throws IllegalArgumentException if the specified radix is out of range. - */ - private static int computeNbrOfDigits_negValue(long negValue, int radix) { - checkRadix(radix); - final int maxNbrOfDigits = MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix]; - long p = radix; - for (int i=1;i -p) { - return i; - } - p *= radix; - } - return maxNbrOfDigits; - } - - /* - * - */ - - private static boolean checkBitPositions(int firstBitPos, int lastBitPosExcl, int bitSize) { - if ((firstBitPos < 0) || (firstBitPos > lastBitPosExcl) || (lastBitPosExcl > bitSize)) { - throw new IllegalArgumentException( - "bit positions (first="+firstBitPos+",lastExcl="+lastBitPosExcl - +") must verify 0 <= first <= lastExcl <= "+bitSize); - } - return true; - } - - /** - * Common method for byte, short and int. - * Could be a bit faster to have specific methods for byte and short, - * but not much, and that would also make more messy (byte-)code. - * - * @param bitSize Must be in [0,32]. - */ - private static String toStringBits_0_32_bitPosAlreadyChecked( - int bitSize, - int bits, - int firstBitPos, - int lastBitPosExcl, - boolean bigEndian, - boolean padding) { - final int bitSizeM1 = bitSize-1; - final int lastBitPos = lastBitPosExcl-1; - if (padding) { - final int nbrOfChars = bitSize; - final char[] chars = new char[nbrOfChars]; - int bitIndex = bitSizeM1; - if (bigEndian) { - final int firstBitIndex = bitSizeM1-lastBitPos; - final int lastBitIndex = bitSizeM1-firstBitPos; - while (bitIndex > lastBitIndex) { - chars[bitSizeM1-bitIndex] = '_'; - --bitIndex; - } - while (bitIndex >= firstBitIndex) { - chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - while (bitIndex >= 0) { - chars[bitSizeM1-bitIndex] = '_'; - --bitIndex; - } - } else { - while (bitIndex > lastBitPos) { - chars[bitIndex] = '_'; - --bitIndex; - } - while (bitIndex >= firstBitPos) { - chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - while (bitIndex >= 0) { - chars[bitIndex] = '_'; - --bitIndex; - } - } - return new String(chars); - } else { - final int nbrOfChars = (lastBitPosExcl - firstBitPos); - final char[] chars = new char[nbrOfChars]; - if (bigEndian) { - final int firstBitIndex = bitSizeM1-lastBitPos; - final int lastBitIndex = bitSizeM1-firstBitPos; - int bitIndex = lastBitIndex; - while (bitIndex >= firstBitIndex) { - chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - } else { - int bitIndex = lastBitPos; - while (bitIndex >= firstBitPos) { - chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1)); - --bitIndex; - } - } - return new String(chars); - } - } -} diff --git a/src/main/java/net/jafama/README.txt b/src/main/java/net/jafama/README.txt deleted file mode 100644 index 287b6e8..0000000 --- a/src/main/java/net/jafama/README.txt +++ /dev/null @@ -1,290 +0,0 @@ -################################################################################ -Jafama 2.2, 2015/12/13 - -Changes since version 2.1: - -- Using a more standard layout for files. -- Renamed AbstractFastMath into CmnFastMath, and factored non floating point - methods common to FastMath and StrictFastMath here, as well as E and PI - constants. - Also factored out corresponding tests in a new test class. - As a result, abs(int) and abs(long) now delegate to Math (and not StrictMath, - which usually delegates to Math for non floating point methods) when EITHER - FastMath or StrictFastMath delegation options are activated. -- (Strict)FastMath: - - Look-up tables are now (by default) lazily initialized, per method type - (i.e. calling sin(double) won't initialize look-up tables for exp(double)). - As a result, methods of (Strict)FastMath that don't use tables can now - safely be called from code that don't want to trigger tables initialization. - - Added a static initTables() method, which ensures their initialization to - avoid related slow-down later at runtime (one call from either FastMath or - StrictFastMath inits tables for both classes). - - Reordered "ifs" in nextAfter(float,double) and nextAfter(double,double), - for faster worse case (as I should already have done when submitting - JDK-8032016). - - In powFast(double,int), reworked "ifs" to reduce the amount of code - while preserving special cases for small powers. Is now also a tad faster. - - Added methods: - - From JDK-8023217: - - multiplyExact(long,int) - - floorDiv(long,int) - - floorMod(long,int) - - From JDK-5100935: - - multiplyFull(int,int) - - multiplyHigh(long,long) - - That were not added previously, for Math versions being preferable due to - eventual JVM intrinsics, but that I finally decided to add for the sake of - completeness: - - incrementExact(int) - - incrementExact(long) - - decrementExact(int) - - decrementExact(long) - - negateExact(int) - - negateExact(long) - - And finally, since we mirror each xxxExact method with a xxxBounded - version: - - multiplyBounded(long,int) - - incrementBounded(int) - - incrementBounded(long) - - decrementBounded(int) - - decrementBounded(long) - - negateBounded(int) - - negateBounded(long) -- NumbersUtils: - - Added methods: - - From (Strict)FastMath, so that they can be used to depart +0.0(f) - from -0.0(f) (which is a quite low-level need) without having to depend - on (Strict)FastMath: - - signFromBit(float) - - signFromBit(double) - - Following request from P. Wendykier: - - twoPowAsIntExact(int) - - twoPowAsIntBounded(int) - - twoPowAsLongExact(int) - - twoPowAsLongBounded(int) - -################################################################################ -Jafama 2.1, 2014/04/30 - -Changes since version 2.0: - -- FastMath: - - Added hypot(double,double,double), which computes sqrt(x^2+y^2+z^2) without - intermediate overflow or underflow. - - Made reduction more accurate for angles of large magnitudes, by using - reduction in [-PI/4,PI/4] and quadrant information stored in two useless - bits of exponent, rather than reduction in [-PI,PI]. - - Added use of TWOPI_LO and TWOPI_HI in some normalization methods, to make - them more accurate for results near quadrants limits. - - Corrected the spec about range of reliable accuracy for sinQuick(double). - - Little optimization for large angles reduction for tan(double). - - For round(float) and round(double), now using algorithm by Dmitry Nadezhin - (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html). - - scalb(double) is now faster for non-huge values of scale factor - (the same optimization, didn't seem to be worth it for float case). - -- Added StrictFastMath, a strict version of FastMath (and not a fast version of - StrictMath!), and related versions of FastMath properties: - - jafama.strict.usejdk, - - jafama.strict.fastlog, - - jafama.strict.fastsqrt. - Note that now, to avoid look-up tables and their initialization overhead, - you must set both jafama.usejdk (which only applies to FastMath) and - jafama.strict.usejdk to true. - -- NumbersUtils: - - Added DOUBLE_MIN_NORMAL and FLOAT_MIN_NORMAL constants (Double.MIN_NORMAL - and Float.MIN_NORMAL being available only from Java 6). - - Added methods: - - absNeg(int) (returns -abs(value), exact even for Integer.MIN_VALUE) - - absNeg(long) (returns -abs(value), exact even for Long.MIN_VALUE) - - pow2_strict(float) - - pow2_strict(double) - - pow3_strict(float) - - pow3_strict(double) - - plus2PI(double) - - plus2PI_strict(double) - - minus2PI(double) - - minus2PI_strict(double) - - plusPI(double) - - plusPI_strict(double) - - minusPI(double) - - minusPI_strict(double) - - plusPIO2(double) - - plusPIO2_strict(double) - - minusPIO2(double) - - minusPIO2_strict(double) - - toStringCSN(double) (to get "1.5E1" or "1.0E-1", instead of "15.0" or - "0.1") - - toStringNoCSN(double) (to get "123456789.0" or "0.0001", instead of - "1.23456789E8" or "1.0E-4") - - Optimized floorPowerOfTwo(int) and ceilingPowerOfTwo(int). - -################################################################################ -Jafama 2.0, 2013/02/20 - -Changes since version 1.2: -- Changed license from GNU LGPL V3 to Apache V2. -- Is now Java 5 compatible (but some unit tests require Java 6). -- Renamed package from odk.lang into net.jafama. -- Renamed properties from odk.fastmath.xxx into jafama.xxx. -- To match upcoming JDK8's new Math methods, renamed: - - toIntSafe into toIntExact (and asInt in NumbersUtils) - - plusNoModulo into addBounded (and plusBounded in NumbersUtils) - - plusNoModuloSafe into addExact (and plusExact in NumbersUtils) - - minusNoModulo into subtractBounded (and minusBounded in NumbersUtils) - - minusNoModuloSafe into subtractExact (and minusExact in NumbersUtils) - - timesNoModulo into multiplyBounded (and timesBounded in NumbersUtils) - - timesNoModuloSafe into multiplyExact (and timesExact in NumbersUtils) -- Corrected Javadoc (accuracy claims) for some xxxQuick methods. -- Removed usage of strictfp, which can cause an overhead. - As a result, behavior might change depending on architecture, - as well as dynamically due to JIT (ex.: pow2(double)). -- Minor typos, refactorings and doc enhancements. -- NumbersUtils: - - Renamed mask methods, such as leftBit0LongMask into longMaskMSBits0. - - Added methods: - - isMathematicalInteger(float) - - isMathematicalInteger(double) - - isEquidistant(float) - - isEquidistant(double) - - isNaNOrInfinite(float) - - Upgraded some implementations. -- FastMath: - - In spec., added warning about possible FastMath slowness and - initialization overhead. - - FastMath now only depends on Java 5, and therefore, when delegating to - Math, only does so for Math methods that exist in Java 5 and have the - same semantics (except for accuracy). - - Modified some treatments for JVM crash after JIT-optimization to - (hopefully) not occur (crashes observed with Java 6u29, with which - workarounds were tested). - - Removed usage of StrictMath, and related property, which is allowed due - to spec. relaxation by strictfp removal, to make things both simpler, - and possibly faster. - As a result, for FastMath.log(double), we now use Math.log(double) by - default instead of our redefined method, which was used before due to - StrictMath.log(double) being possibly very slow. This makes log10(double), - log1p(double), logQuick(double), pow(double,double) and - powQuick(double,double) more accurate. - - Changed sinAndCos(double,DoubleWrapper,DoubleWrapper) into - sinAndCos(double,DoubleWrapper), and - sinhAndCosh(double,DoubleWrapper,DoubleWrapper) into - sinhAndCosh(double,DoubleWrapper). - - exp(double) is now more accurate (removed special handling for subnormals, - which is useless with proper multiplications order). - This makes hyperbolic trigonometry functions, expm1(double), - pow(double,double) and powQuick(double,double) more accurate. - - round(float) and round(double) now no longer follow Math class, which - spec. and behavior changed over time, but just round-up properly. - - For asinInRange and acosInRange, replaced < and > with <= and >=, - for quick return in case input is a bound. - - Removed internal usage of look-up table to compute double powers of two, - for it doesn't speed things up much, and to avoid possible cache-misses - for methods that are now table-free. - - Added methods: - - coshm1(double) - - asinh(double) - - acosh(double) - - acosh1p(double) - - atanh(double) - - log10(double) - - sqrtQuick(double) - - invSqrtQuick(double) - - roundEven(float) - - roundEven(double) - - rint(float) - - rint(double) - - abs(long) - - floorDiv(int,int) - - floorDiv(long,long) - - floorMod(int,int) - - floorMod(long,long) - - isNaNOrInfinite(float) - - signum(float) - - signum(double) - - signFromBit(float) - - signFromBit(double) - - copySign(float,float) - - copySign(double,double) - - ulp(float) - - ulp(double) - - nextAfter(float,double) - - nextAfter(double,double) - - nextDown(float) - - nextDown(double) - - nextUp(float) - - nextUp(double) - - scalb(float,int) - - scalb(double,int) - - Separated accuracy/correctness tests from benches, and - improved/simplified both. - -################################################################################ -Jafama 1.2, 2011/03/19 - -Changes since version 1.1: -- Now using StrictMath to compute constants and look-up tables, to ensure - consistency across various architectures. -- Now using Math.abs(double) directly instead of FastMath.abs(double), since - this method is not redefined. -- Added PI_SUP constant, the closest upper approximation of Pi as double, - especially useful to define a span that covers full angular range - (2*Math.PI doesn't). -- Added log2(long), log2(int). -- Added odk.fastmath.strict, odk.fastmath.usejdk, odk.fastmath.fastlog and - odk.fastmath.fastsqrt properties. See FastMath Javadoc for details. - NB: As a consequence, by default, a redefined log(double) is now used instead - of Math.log(double), for non-redefined treatments now use StrictMath by - default, and StrictMath.log(double) seems usually slow. -- Simplified toString() implementation for IntWrapper and DoubleWrapper classes. -- Completed Javadoc and updated tests for FastMath.remainder(double,double) - method, which does not behave as Math.IEEEremainder(double,double). -- Moved some basic numbers related treatments, into a new class (NumbersUtils), - since they are very low-level and can be used in many places where a - dependency to the heavy (look-up tables) FastMath class could be considered - inappropriate. - These treatments are still available from FastMath class. -- In benches, made sure dummy variables are used, to avoid treatments to be - optimized away (has not been observed, but might have been with some JVMs). - -################################################################################ -Jafama 1.1, 2009/12/05 - -Changes since version 1.0: -- for asin pow tabs, use of powFast(double,int) instead of pow(double,double), -- added expQuick(double), logQuick(double), powQuick(double), -- changed random numbers computation for tests. - -################################################################################ -Jafama 1.0, 2009/07/25 - -- Placed under the GNU Lesser General Public License, version 3. - -- Requires Java 1.6 or later. - -- src folder contains the code. - -- test folder contains some tests (some of which require JUnit). - -- The odk.lang package is due to this code being a core part of ODK - library (Optimized Development Kit, of which only this code is - open source). - -- Copy/paste of FastMath class comments: - - * Class providing math treatments that: - * - are meant to be faster than those of java.lang.Math class (depending on - * JVM or JVM options, they might be slower), - * - are still somehow accurate and robust (handling of NaN and such), - * - do not (or not directly) generate objects at run time (no "new"). - * - * Other than optimized treatments, a valuable feature of this class is the - * presence of angles normalization methods, derived from those used in - * java.lang.Math (for which, sadly, no API is provided, letting everyone - * with the terrible responsibility to write their own ones). - * - * Non-redefined methods of java.lang.Math class are also available, - * for easy replacement. - -################################################################################ diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheByte.java b/src/main/java/sun/java2d/marlin/ArrayCacheByte.java index 9aab566..6f4eb7e 100644 --- a/src/main/java/sun/java2d/marlin/ArrayCacheByte.java +++ b/src/main/java/sun/java2d/marlin/ArrayCacheByte.java @@ -28,11 +28,13 @@ import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; + import static sun.java2d.marlin.MarlinConst.DO_STATS; import static sun.java2d.marlin.MarlinConst.DO_CHECKS; import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY; import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY; import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE; + import static sun.java2d.marlin.MarlinUtils.logInfo; import static sun.java2d.marlin.MarlinUtils.logException; @@ -48,9 +50,6 @@ * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java * files are generated with the following command lines: */ -// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheFloat.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheDouble.java final class ArrayCacheByte { diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java b/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java index 10db7f7..5857ebf 100644 --- a/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java +++ b/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java @@ -28,11 +28,13 @@ import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; + import static sun.java2d.marlin.MarlinConst.DO_STATS; import static sun.java2d.marlin.MarlinConst.DO_CHECKS; import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY; import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY; import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE; + import static sun.java2d.marlin.MarlinUtils.logInfo; import static sun.java2d.marlin.MarlinUtils.logException; @@ -48,9 +50,6 @@ * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java * files are generated with the following command lines: */ -// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheFloat.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheDouble.java final class ArrayCacheDouble { diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java b/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java deleted file mode 100644 index 9fda05d..0000000 --- a/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; -import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; -import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; -import static sun.java2d.marlin.MarlinConst.DO_STATS; -import static sun.java2d.marlin.MarlinConst.DO_CHECKS; -import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY; -import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY; -import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE; -import static sun.java2d.marlin.MarlinUtils.logInfo; -import static sun.java2d.marlin.MarlinUtils.logException; - -import java.lang.ref.WeakReference; -import java.util.Arrays; - -import sun.java2d.marlin.ArrayCacheConst.BucketStats; -import sun.java2d.marlin.ArrayCacheConst.CacheStats; - -/* - * Note that the ArrayCache[BYTE/INT/FLOAT/DOUBLE] files are nearly identical except - * for a few type and name differences. Typically, the [BYTE]ArrayCache.java file - * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java - * files are generated with the following command lines: - */ -// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheFloat.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheDouble.java - -final class ArrayCacheFloat { - - /* members */ - private final int bucketCapacity; - private WeakReference refBuckets = null; - final CacheStats stats; - - ArrayCacheFloat(final int bucketCapacity) { - this.bucketCapacity = bucketCapacity; - this.stats = (DO_STATS) ? - new CacheStats("ArrayCacheFloat(Dirty)") : null; - } - - Bucket getCacheBucket(final int length) { - final int bucket = ArrayCacheConst.getBucket(length); - return getBuckets()[bucket]; - } - - private Bucket[] getBuckets() { - // resolve reference: - Bucket[] buckets = (refBuckets != null) ? refBuckets.get() : null; - - // create a new buckets ? - if (buckets == null) { - buckets = new Bucket[BUCKETS]; - - for (int i = 0; i < BUCKETS; i++) { - buckets[i] = new Bucket(ARRAY_SIZES[i], bucketCapacity, - (DO_STATS) ? stats.bucketStats[i] : null); - } - - // update weak reference: - refBuckets = new WeakReference(buckets); - } - return buckets; - } - - Reference createRef(final int initialSize) { - return new Reference(this, initialSize); - } - - static final class Reference { - - // initial array reference (direct access) - final float[] initial; - private final ArrayCacheFloat cache; - - Reference(final ArrayCacheFloat cache, final int initialSize) { - this.cache = cache; - this.initial = createArray(initialSize); - if (DO_STATS) { - cache.stats.totalInitial += initialSize; - } - } - - float[] getArray(final int length) { - if (length <= MAX_ARRAY_SIZE) { - return cache.getCacheBucket(length).getArray(); - } - if (DO_STATS) { - cache.stats.oversize++; - } - if (DO_LOG_OVERSIZE) { - logInfo("ArrayCacheFloat(Dirty): " - + "getArray[oversize]: length=\t" + length); - } - return createArray(length); - } - - float[] widenArray(final float[] array, final int usedSize, - final int needSize) - { - final int length = array.length; - if (DO_CHECKS && length >= needSize) { - return array; - } - if (DO_STATS) { - cache.stats.resize++; - } - - // maybe change bucket: - // ensure getNewSize() > newSize: - final float[] res = getArray(ArrayCacheConst.getNewSize(usedSize, needSize)); - - // use wrapper to ensure proper copy: - System.arraycopy(array, 0, res, 0, usedSize); // copy only used elements - - // maybe return current array: - putArray(array, 0, usedSize); // ensure array is cleared - - if (DO_LOG_WIDEN_ARRAY) { - logInfo("ArrayCacheFloat(Dirty): " - + "widenArray[" + res.length - + "]: usedSize=\t" + usedSize + "\tlength=\t" + length - + "\tneeded length=\t" + needSize); - } - return res; - } - - boolean doCleanRef(final float[] array) { - return DO_CLEAN_DIRTY || (array != initial); - } - - float[] putArray(final float[] array) - { - // dirty array helper: - return putArray(array, 0, array.length); - } - - float[] putArray(final float[] array, final int fromIndex, - final int toIndex) - { - if (array.length <= MAX_ARRAY_SIZE) { - if (DO_CLEAN_DIRTY && (toIndex != 0)) { - // clean-up array of dirty part[fromIndex; toIndex[ - fill(array, fromIndex, toIndex, 0.0f); - } - // ensure to never store initial arrays in cache: - if (array != initial) { - cache.getCacheBucket(array.length).putArray(array); - } - } - return initial; - } - } - - static final class Bucket { - - private int tail = 0; - private final int arraySize; - private final float[][] arrays; - private final BucketStats stats; - - Bucket(final int arraySize, - final int capacity, final BucketStats stats) - { - this.arraySize = arraySize; - this.stats = stats; - this.arrays = new float[capacity][]; - } - - float[] getArray() { - if (DO_STATS) { - stats.getOp++; - } - // use cache: - if (tail != 0) { - final float[] array = arrays[--tail]; - arrays[tail] = null; - return array; - } - if (DO_STATS) { - stats.createOp++; - } - return createArray(arraySize); - } - - void putArray(final float[] array) - { - if (DO_CHECKS && (array.length != arraySize)) { - logInfo("ArrayCacheFloat(Dirty): " - + "bad length = " + array.length); - return; - } - if (DO_STATS) { - stats.returnOp++; - } - // fill cache: - if (arrays.length > tail) { - arrays[tail++] = array; - - if (DO_STATS) { - stats.updateMaxSize(tail); - } - } else if (DO_CHECKS) { - logInfo("ArrayCacheFloat(Dirty): " - + "array capacity exceeded !"); - } - } - } - - static float[] createArray(final int length) { - return new float[length]; - } - - static void fill(final float[] array, final int fromIndex, - final int toIndex, final float value) - { - // clear array data: - Arrays.fill(array, fromIndex, toIndex, value); - if (DO_CHECKS) { - check(array, fromIndex, toIndex, value); - } - } - - static void check(final float[] array, final int fromIndex, - final int toIndex, final float value) - { - if (DO_CHECKS) { - // check zero on full array: - for (int i = 0; i < array.length; i++) { - if (array[i] != value) { - logException("Invalid value at: " + i + " = " + array[i] - + " from: " + fromIndex + " to: " + toIndex + "\n" - + Arrays.toString(array), new Throwable()); - - // ensure array is correctly filled: - Arrays.fill(array, value); - - return; - } - } - } - } -} diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheInt.java b/src/main/java/sun/java2d/marlin/ArrayCacheInt.java index 299f5c6..a4dc2d2 100644 --- a/src/main/java/sun/java2d/marlin/ArrayCacheInt.java +++ b/src/main/java/sun/java2d/marlin/ArrayCacheInt.java @@ -28,11 +28,13 @@ import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; + import static sun.java2d.marlin.MarlinConst.DO_STATS; import static sun.java2d.marlin.MarlinConst.DO_CHECKS; import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY; import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY; import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE; + import static sun.java2d.marlin.MarlinUtils.logInfo; import static sun.java2d.marlin.MarlinUtils.logException; @@ -48,9 +50,6 @@ * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java * files are generated with the following command lines: */ -// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheFloat.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheDouble.java final class ArrayCacheInt { diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java b/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java index f41c858..00d0bda 100644 --- a/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java +++ b/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java @@ -28,10 +28,12 @@ import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; + import static sun.java2d.marlin.MarlinConst.DO_STATS; import static sun.java2d.marlin.MarlinConst.DO_CHECKS; import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY; import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE; + import static sun.java2d.marlin.MarlinUtils.logInfo; import static sun.java2d.marlin.MarlinUtils.logException; @@ -47,9 +49,6 @@ * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java * files are generated with the following command lines: */ -// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheFloat.java -// % 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' < ArrayCacheB\yte.java > ArrayCacheDouble.java final class ArrayCacheIntClean { diff --git a/src/main/java/sun/java2d/marlin/CollinearSimplifier.java b/src/main/java/sun/java2d/marlin/CollinearSimplifier.java index 4d162f9..75bc8aa 100644 --- a/src/main/java/sun/java2d/marlin/CollinearSimplifier.java +++ b/src/main/java/sun/java2d/marlin/CollinearSimplifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,27 @@ package sun.java2d.marlin; -import sun.awt.geom.PathConsumer2D; -final class CollinearSimplifier implements PathConsumer2D { +final class CollinearSimplifier implements DPathConsumer2D { private static final int STATE_PREV_LINE = 0; private static final int STATE_PREV_POINT = 1; private static final int STATE_EMPTY = 2; // slope precision threshold - private static final float EPS = 1e-3f; // aaime proposed 1e-3f + private static final double EPS = 1e-3d; // aaime proposed 1e-3d // members: - private PathConsumer2D delegate; + private DPathConsumer2D delegate; private int state; - private float px1, py1; - private float pdx, pdy; - private float px2, py2; + private double px1, py1; + private double pdx, pdy; + private double px2, py2; CollinearSimplifier() { } - public CollinearSimplifier init(final PathConsumer2D delegate) { + public CollinearSimplifier init(final DPathConsumer2D delegate) { if (this.delegate != delegate) { this.delegate = delegate; } @@ -75,8 +74,8 @@ public long getNativeConsumer() { } @Override - public void quadTo(final float x1, final float y1, - final float xe, final float ye) + public void quadTo(final double x1, final double y1, + final double xe, final double ye) { emitStashedLine(); delegate.quadTo(x1, y1, xe, ye); @@ -87,9 +86,9 @@ public void quadTo(final float x1, final float y1, } @Override - public void curveTo(final float x1, final float y1, - final float x2, final float y2, - final float xe, final float ye) + public void curveTo(final double x1, final double y1, + final double x2, final double y2, + final double xe, final double ye) { emitStashedLine(); delegate.curveTo(x1, y1, x2, y2, xe, ye); @@ -100,7 +99,7 @@ public void curveTo(final float x1, final float y1, } @Override - public void moveTo(final float xe, final float ye) { + public void moveTo(final double xe, final double ye) { emitStashedLine(); delegate.moveTo(xe, ye); state = STATE_PREV_POINT; @@ -109,19 +108,19 @@ public void moveTo(final float xe, final float ye) { } @Override - public void lineTo(final float xe, final float ye) { + public void lineTo(final double xe, final double ye) { // most probable case first: if (state == STATE_PREV_LINE) { // test for collinearity - final float dx = (xe - px2); - final float dy = (ye - py2); + final double dx = (xe - px2); + final double dy = (ye - py2); // perf: avoid slope computation (fdiv) replaced by 3 fmul - if ((dy == 0.0f && pdy == 0.0f && (pdx * dx) >= 0.0f) + if ((dy == 0.0d && pdy == 0.0d && (pdx * dx) >= 0.0d) // uncertainty on slope: // || (Math.abs(pdx * dy - pdy * dx) < EPS * Math.abs(pdy * dy))) { // try 0 - || ((pdy * dy) != 0.0f && (pdx * dy - pdy * dx) == 0.0f)) { + || ((pdy * dy) != 0.0d && (pdx * dy - pdy * dx) == 0.0d)) { // same horizontal orientation or same slope: // TODO: store cumulated error on slope ? // merge segments diff --git a/src/main/java/sun/java2d/marlin/Curve.java b/src/main/java/sun/java2d/marlin/Curve.java index c904f3e..2ce0cd4 100644 --- a/src/main/java/sun/java2d/marlin/Curve.java +++ b/src/main/java/sun/java2d/marlin/Curve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,13 @@ final class Curve { - float ax, ay, bx, by, cx, cy, dx, dy; - float dax, day, dbx, dby; + double ax, ay, bx, by, cx, cy, dx, dy; + double dax, day, dbx, dby; Curve() { } - void set(final float[] points, final int type) { + void set(final double[] points, final int type) { // if instead of switch (perf + most probable cases first) if (type == 8) { set(points[0], points[1], @@ -50,15 +50,15 @@ void set(final float[] points, final int type) { } } - void set(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3, - final float x4, final float y4) + void set(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3, + final double x4, final double y4) { - final float dx32 = 3.0f * (x3 - x2); - final float dy32 = 3.0f * (y3 - y2); - final float dx21 = 3.0f * (x2 - x1); - final float dy21 = 3.0f * (y2 - y1); + final double dx32 = 3.0d * (x3 - x2); + final double dy32 = 3.0d * (y3 - y2); + final double dx21 = 3.0d * (x2 - x1); + final double dy21 = 3.0d * (y2 - y1); ax = (x4 - x1) - dx32; // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2) ay = (y4 - y1) - dy32; bx = (dx32 - dx21); // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1 @@ -67,95 +67,95 @@ void set(final float x1, final float y1, cy = dy21; dx = x1; // D = P0 dy = y1; - dax = 3.0f * ax; - day = 3.0f * ay; - dbx = 2.0f * bx; - dby = 2.0f * by; + dax = 3.0d * ax; + day = 3.0d * ay; + dbx = 2.0d * bx; + dby = 2.0d * by; } - void set(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + void set(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { - final float dx21 = (x2 - x1); - final float dy21 = (y2 - y1); - ax = 0.0f; // A = 0 - ay = 0.0f; + final double dx21 = (x2 - x1); + final double dy21 = (y2 - y1); + ax = 0.0d; // A = 0 + ay = 0.0d; bx = (x3 - x2) - dx21; // B = P3 - P0 - 2 P2 by = (y3 - y2) - dy21; - cx = 2.0f * dx21; // C = 2 (P2 - P1) - cy = 2.0f * dy21; + cx = 2.0d * dx21; // C = 2 (P2 - P1) + cy = 2.0d * dy21; dx = x1; // D = P1 dy = y1; - dax = 0.0f; - day = 0.0f; - dbx = 2.0f * bx; - dby = 2.0f * by; + dax = 0.0d; + day = 0.0d; + dbx = 2.0d * bx; + dby = 2.0d * by; } - void set(final float x1, final float y1, - final float x2, final float y2) + void set(final double x1, final double y1, + final double x2, final double y2) { - final float dx21 = (x2 - x1); - final float dy21 = (y2 - y1); - ax = 0.0f; // A = 0 - ay = 0.0f; - bx = 0.0f; // B = 0 - by = 0.0f; + final double dx21 = (x2 - x1); + final double dy21 = (y2 - y1); + ax = 0.0d; // A = 0 + ay = 0.0d; + bx = 0.0d; // B = 0 + by = 0.0d; cx = dx21; // C = (P2 - P1) cy = dy21; dx = x1; // D = P1 dy = y1; - dax = 0.0f; - day = 0.0f; - dbx = 0.0f; - dby = 0.0f; + dax = 0.0d; + day = 0.0d; + dbx = 0.0d; + dby = 0.0d; } - int dxRoots(final float[] roots, final int off) { + int dxRoots(final double[] roots, final int off) { return Helpers.quadraticRoots(dax, dbx, cx, roots, off); } - int dyRoots(final float[] roots, final int off) { + int dyRoots(final double[] roots, final int off) { return Helpers.quadraticRoots(day, dby, cy, roots, off); } - int infPoints(final float[] pts, final int off) { + int infPoints(final double[] pts, final int off) { // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0 // Fortunately, this turns out to be quadratic, so there are at // most 2 inflection points. - final float a = dax * dby - dbx * day; - final float b = 2.0f * (cy * dax - day * cx); - final float c = cy * dbx - cx * dby; + final double a = dax * dby - dbx * day; + final double b = 2.0d * (cy * dax - day * cx); + final double c = cy * dbx - cx * dby; return Helpers.quadraticRoots(a, b, c, pts, off); } - int xPoints(final float[] ts, final int off, final float x) + int xPoints(final double[] ts, final int off, final double x) { - return Helpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0f, 1.0f); + return Helpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0d, 1.0d); } - int yPoints(final float[] ts, final int off, final float y) + int yPoints(final double[] ts, final int off, final double y) { - return Helpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0f, 1.0f); + return Helpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0d, 1.0d); } // finds points where the first and second derivative are // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where // * is a dot product). Unfortunately, we have to solve a cubic. - private int perpendiculardfddf(final float[] pts, final int off) { + private int perpendiculardfddf(final double[] pts, final int off) { assert pts.length >= off + 4; // these are the coefficients of some multiple of g(t) (not g(t), // because the roots of a polynomial are not changed after multiplication // by a constant, and this way we save a few multiplications). - final float a = 2.0f * (dax * dax + day * day); - final float b = 3.0f * (dax * dbx + day * dby); - final float c = 2.0f * (dax * cx + day * cy) + dbx * dbx + dby * dby; - final float d = dbx * cx + dby * cy; + final double a = 2.0d * (dax * dax + day * day); + final double b = 3.0d * (dax * dbx + day * dby); + final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby; + final double d = dbx * cx + dby * cy; - return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0f, 1.0f); + return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d); } // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses @@ -171,21 +171,21 @@ private int perpendiculardfddf(final float[] pts, final int off) { // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection // points, so roc-w can have at least 6 roots. This shouldn't be a // problem for what we're trying to do (draw a nice looking curve). - int rootsOfROCMinusW(final float[] roots, final int off, final float w2, final float err) { + int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) { // no OOB exception, because by now off<=6, and roots.length >= 10 assert off <= 6 && roots.length >= 10; int ret = off; final int end = off + perpendiculardfddf(roots, off); - roots[end] = 1.0f; // always check interval end points + roots[end] = 1.0d; // always check interval end points - float t0 = 0.0f, ft0 = ROCsq(t0) - w2; + double t0 = 0.0d, ft0 = ROCsq(t0) - w2; for (int i = off; i <= end; i++) { - float t1 = roots[i], ft1 = ROCsq(t1) - w2; - if (ft0 == 0.0f) { + double t1 = roots[i], ft1 = ROCsq(t1) - w2; + if (ft0 == 0.0d) { roots[ret++] = t0; - } else if (ft1 * ft0 < 0.0f) { // have opposite signs + } else if (ft1 * ft0 < 0.0d) { // have opposite signs // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because // ROC(t) >= 0 for all t. roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err); @@ -197,9 +197,9 @@ int rootsOfROCMinusW(final float[] roots, final int off, final float w2, final f return ret - off; } - private static float eliminateInf(final float x) { - return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE : - (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x)); + private static double eliminateInf(final double x) { + return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE : + (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x)); } // A slight modification of the false position algorithm on wikipedia. @@ -209,14 +209,14 @@ private static float eliminateInf(final float x) { // expressions make it into the language), depending on how closures // and turn out. Same goes for the newton's method // algorithm in Helpers.java - private float falsePositionROCsqMinusX(final float t0, final float t1, - final float w2, final float err) + private double falsePositionROCsqMinusX(final double t0, final double t1, + final double w2, final double err) { final int iterLimit = 100; int side = 0; - float t = t1, ft = eliminateInf(ROCsq(t) - w2); - float s = t0, fs = eliminateInf(ROCsq(s) - w2); - float r = s, fr; + double t = t1, ft = eliminateInf(ROCsq(t) - w2); + double s = t0, fs = eliminateInf(ROCsq(s) - w2); + double r = s, fr; for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { r = (fs * t - ft * s) / (fs - ft); @@ -229,7 +229,7 @@ private float falsePositionROCsqMinusX(final float t0, final float t1, } else { side = -1; } - } else if (fr * fs > 0.0f) { + } else if (fr * fs > 0.0d) { fs = fr; s = r; if (side > 0) { ft /= (1 << side); @@ -244,21 +244,21 @@ private float falsePositionROCsqMinusX(final float t0, final float t1, return r; } - private static boolean sameSign(final float x, final float y) { + private static boolean sameSign(final double x, final double y) { // another way is to test if x*y > 0. This is bad for small x, y. - return (x < 0.0f && y < 0.0f) || (x > 0.0f && y > 0.0f); + return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d); } // returns the radius of curvature squared at t of this curve // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications) - private float ROCsq(final float t) { - final float dx = t * (t * dax + dbx) + cx; - final float dy = t * (t * day + dby) + cy; - final float ddx = 2.0f * dax * t + dbx; - final float ddy = 2.0f * day * t + dby; - final float dx2dy2 = dx * dx + dy * dy; - final float ddx2ddy2 = ddx * ddx + ddy * ddy; - final float ddxdxddydy = ddx * dx + ddy * dy; + private double ROCsq(final double t) { + final double dx = t * (t * dax + dbx) + cx; + final double dy = t * (t * day + dby) + cy; + final double ddx = 2.0d * dax * t + dbx; + final double ddy = 2.0d * day * t + dby; + final double dx2dy2 = dx * dx + dy * dy; + final double ddx2ddy2 = ddx * ddx + ddy * ddy; + final double ddxdxddydy = ddx * dx + ddy * dy; return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy)); } } diff --git a/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java b/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java deleted file mode 100644 index 22b5c9e..0000000 --- a/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - - -final class DCollinearSimplifier implements DPathConsumer2D { - - private static final int STATE_PREV_LINE = 0; - private static final int STATE_PREV_POINT = 1; - private static final int STATE_EMPTY = 2; - - // slope precision threshold - private static final double EPS = 1e-3d; // aaime proposed 1e-3d - - // members: - private DPathConsumer2D delegate; - private int state; - private double px1, py1; - private double pdx, pdy; - private double px2, py2; - - DCollinearSimplifier() { - } - - public DCollinearSimplifier init(final DPathConsumer2D delegate) { - if (this.delegate != delegate) { - this.delegate = delegate; - } - this.state = STATE_EMPTY; - - return this; // fluent API - } - - @Override - public void pathDone() { - emitStashedLine(); - delegate.pathDone(); - state = STATE_EMPTY; - } - - @Override - public void closePath() { - emitStashedLine(); - delegate.closePath(); - state = STATE_EMPTY; - } - - @Override - public long getNativeConsumer() { - return 0; - } - - @Override - public void quadTo(final double x1, final double y1, - final double xe, final double ye) - { - emitStashedLine(); - delegate.quadTo(x1, y1, xe, ye); - // final end point: - state = STATE_PREV_POINT; - px1 = xe; - py1 = ye; - } - - @Override - public void curveTo(final double x1, final double y1, - final double x2, final double y2, - final double xe, final double ye) - { - emitStashedLine(); - delegate.curveTo(x1, y1, x2, y2, xe, ye); - // final end point: - state = STATE_PREV_POINT; - px1 = xe; - py1 = ye; - } - - @Override - public void moveTo(final double xe, final double ye) { - emitStashedLine(); - delegate.moveTo(xe, ye); - state = STATE_PREV_POINT; - px1 = xe; - py1 = ye; - } - - @Override - public void lineTo(final double xe, final double ye) { - // most probable case first: - if (state == STATE_PREV_LINE) { - // test for collinearity - final double dx = (xe - px2); - final double dy = (ye - py2); - - // perf: avoid slope computation (fdiv) replaced by 3 fmul - if ((dy == 0.0d && pdy == 0.0d && (pdx * dx) >= 0.0d) -// uncertainty on slope: -// || (Math.abs(pdx * dy - pdy * dx) < EPS * Math.abs(pdy * dy))) { -// try 0 - || ((pdy * dy) != 0.0d && (pdx * dy - pdy * dx) == 0.0d)) { - // same horizontal orientation or same slope: - // TODO: store cumulated error on slope ? - // merge segments - px2 = xe; - py2 = ye; - } else { - // emit previous segment - delegate.lineTo(px2, py2); - px1 = px2; - py1 = py2; - pdx = dx; - pdy = dy; - px2 = xe; - py2 = ye; - } - } else if (state == STATE_PREV_POINT) { - state = STATE_PREV_LINE; - pdx = (xe - px1); - pdy = (ye - py1); - px2 = xe; - py2 = ye; - } else if (state == STATE_EMPTY) { - delegate.lineTo(xe, ye); - state = STATE_PREV_POINT; - px1 = xe; - py1 = ye; - } - } - - private void emitStashedLine() { - if (state == STATE_PREV_LINE) { - delegate.lineTo(px2, py2); - } - } -} diff --git a/src/main/java/sun/java2d/marlin/DCurve.java b/src/main/java/sun/java2d/marlin/DCurve.java deleted file mode 100644 index 7e06031..0000000 --- a/src/main/java/sun/java2d/marlin/DCurve.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -final class DCurve { - - double ax, ay, bx, by, cx, cy, dx, dy; - double dax, day, dbx, dby; - - DCurve() { - } - - void set(final double[] points, final int type) { - // if instead of switch (perf + most probable cases first) - if (type == 8) { - set(points[0], points[1], - points[2], points[3], - points[4], points[5], - points[6], points[7]); - } else if (type == 4) { - set(points[0], points[1], - points[2], points[3]); - } else { - set(points[0], points[1], - points[2], points[3], - points[4], points[5]); - } - } - - void set(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3, - final double x4, final double y4) - { - final double dx32 = 3.0d * (x3 - x2); - final double dy32 = 3.0d * (y3 - y2); - final double dx21 = 3.0d * (x2 - x1); - final double dy21 = 3.0d * (y2 - y1); - ax = (x4 - x1) - dx32; // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2) - ay = (y4 - y1) - dy32; - bx = (dx32 - dx21); // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1 - by = (dy32 - dy21); - cx = dx21; // C = 3 (P1 - P0) - cy = dy21; - dx = x1; // D = P0 - dy = y1; - dax = 3.0d * ax; - day = 3.0d * ay; - dbx = 2.0d * bx; - dby = 2.0d * by; - } - - void set(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final double dx21 = (x2 - x1); - final double dy21 = (y2 - y1); - ax = 0.0d; // A = 0 - ay = 0.0d; - bx = (x3 - x2) - dx21; // B = P3 - P0 - 2 P2 - by = (y3 - y2) - dy21; - cx = 2.0d * dx21; // C = 2 (P2 - P1) - cy = 2.0d * dy21; - dx = x1; // D = P1 - dy = y1; - dax = 0.0d; - day = 0.0d; - dbx = 2.0d * bx; - dby = 2.0d * by; - } - - void set(final double x1, final double y1, - final double x2, final double y2) - { - final double dx21 = (x2 - x1); - final double dy21 = (y2 - y1); - ax = 0.0d; // A = 0 - ay = 0.0d; - bx = 0.0d; // B = 0 - by = 0.0d; - cx = dx21; // C = (P2 - P1) - cy = dy21; - dx = x1; // D = P1 - dy = y1; - dax = 0.0d; - day = 0.0d; - dbx = 0.0d; - dby = 0.0d; - } - - int dxRoots(final double[] roots, final int off) { - return DHelpers.quadraticRoots(dax, dbx, cx, roots, off); - } - - int dyRoots(final double[] roots, final int off) { - return DHelpers.quadraticRoots(day, dby, cy, roots, off); - } - - int infPoints(final double[] pts, final int off) { - // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0 - // Fortunately, this turns out to be quadratic, so there are at - // most 2 inflection points. - final double a = dax * dby - dbx * day; - final double b = 2.0d * (cy * dax - day * cx); - final double c = cy * dbx - cx * dby; - - return DHelpers.quadraticRoots(a, b, c, pts, off); - } - - int xPoints(final double[] ts, final int off, final double x) - { - return DHelpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0d, 1.0d); - } - - int yPoints(final double[] ts, final int off, final double y) - { - return DHelpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0d, 1.0d); - } - - // finds points where the first and second derivative are - // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where - // * is a dot product). Unfortunately, we have to solve a cubic. - private int perpendiculardfddf(final double[] pts, final int off) { - assert pts.length >= off + 4; - - // these are the coefficients of some multiple of g(t) (not g(t), - // because the roots of a polynomial are not changed after multiplication - // by a constant, and this way we save a few multiplications). - final double a = 2.0d * (dax * dax + day * day); - final double b = 3.0d * (dax * dbx + day * dby); - final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby; - final double d = dbx * cx + dby * cy; - - return DHelpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d); - } - - // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses - // a variant of the false position algorithm to find the roots. False - // position requires that 2 initial values x0,x1 be given, and that the - // function must have opposite signs at those values. To find such - // values, we need the local extrema of the ROC function, for which we - // need the roots of its derivative; however, it's harder to find the - // roots of the derivative in this case than it is to find the roots - // of the original function. So, we find all points where this curve's - // first and second derivative are perpendicular, and we pretend these - // are our local extrema. There are at most 3 of these, so we will check - // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection - // points, so roc-w can have at least 6 roots. This shouldn't be a - // problem for what we're trying to do (draw a nice looking curve). - int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) { - // no OOB exception, because by now off<=6, and roots.length >= 10 - assert off <= 6 && roots.length >= 10; - - int ret = off; - final int end = off + perpendiculardfddf(roots, off); - roots[end] = 1.0d; // always check interval end points - - double t0 = 0.0d, ft0 = ROCsq(t0) - w2; - - for (int i = off; i <= end; i++) { - double t1 = roots[i], ft1 = ROCsq(t1) - w2; - if (ft0 == 0.0d) { - roots[ret++] = t0; - } else if (ft1 * ft0 < 0.0d) { // have opposite signs - // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because - // ROC(t) >= 0 for all t. - roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err); - } - t0 = t1; - ft0 = ft1; - } - - return ret - off; - } - - private static double eliminateInf(final double x) { - return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE : - (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x)); - } - - // A slight modification of the false position algorithm on wikipedia. - // This only works for the ROCsq-x functions. It might be nice to have - // the function as an argument, but that would be awkward in java6. - // TODO: It is something to consider for java8 (or whenever lambda - // expressions make it into the language), depending on how closures - // and turn out. Same goes for the newton's method - // algorithm in DHelpers.java - private double falsePositionROCsqMinusX(final double t0, final double t1, - final double w2, final double err) - { - final int iterLimit = 100; - int side = 0; - double t = t1, ft = eliminateInf(ROCsq(t) - w2); - double s = t0, fs = eliminateInf(ROCsq(s) - w2); - double r = s, fr; - - for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { - r = (fs * t - ft * s) / (fs - ft); - fr = ROCsq(r) - w2; - if (sameSign(fr, ft)) { - ft = fr; t = r; - if (side < 0) { - fs /= (1 << (-side)); - side--; - } else { - side = -1; - } - } else if (fr * fs > 0.0d) { - fs = fr; s = r; - if (side > 0) { - ft /= (1 << side); - side++; - } else { - side = 1; - } - } else { - break; - } - } - return r; - } - - private static boolean sameSign(final double x, final double y) { - // another way is to test if x*y > 0. This is bad for small x, y. - return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d); - } - - // returns the radius of curvature squared at t of this curve - // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications) - private double ROCsq(final double t) { - final double dx = t * (t * dax + dbx) + cx; - final double dy = t * (t * day + dby) + cy; - final double ddx = 2.0d * dax * t + dbx; - final double ddy = 2.0d * day * t + dby; - final double dx2dy2 = dx * dx + dy * dy; - final double ddx2ddy2 = ddx * ddx + ddy * ddy; - final double ddxdxddydy = ddx * dx + ddy * dy; - return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy)); - } -} diff --git a/src/main/java/sun/java2d/marlin/DDasher.java b/src/main/java/sun/java2d/marlin/DDasher.java deleted file mode 100644 index e994c37..0000000 --- a/src/main/java/sun/java2d/marlin/DDasher.java +++ /dev/null @@ -1,1136 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.util.Arrays; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter; - -/** - * The DDasher class takes a series of linear commands - * (moveTo, lineTo, close and - * end) and breaks them into smaller segments according to a - * dash pattern array and a starting dash phase. - * - *

Issues: in J2Se, a zero length dash segment as drawn as a very - * short dash, whereas Pisces does not draw anything. The PostScript - * semantics are unclear. - * - */ -final class DDasher implements DPathConsumer2D, MarlinConst { - - /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */ - static final int REC_LIMIT = 16; - static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial - static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT); - - static final double EPS = 1e-6d; - - // More than 24 bits of mantissa means we can no longer accurately - // measure the number of times cycled through the dash array so we - // punt and override the phase to just be 0 past that point. - static final double MAX_CYCLES = 16000000.0d; - - private DPathConsumer2D out; - private double[] dash; - private int dashLen; - private double startPhase; - private boolean startDashOn; - private int startIdx; - - private boolean starting; - private boolean needsMoveTo; - - private int idx; - private boolean dashOn; - private double phase; - - // The starting point of the path - private double sx0, sy0; - // the current point - private double cx0, cy0; - - // temporary storage for the current curve - private final double[] curCurvepts; - - // per-thread renderer context - final DRendererContext rdrCtx; - - // flag to recycle dash array copy - boolean recycleDashes; - - // We don't emit the first dash right away. If we did, caps would be - // drawn on it, but we need joins to be drawn if there's a closePath() - // So, we store the path elements that make up the first dash in the - // buffer below. - private double[] firstSegmentsBuffer; // dynamic array - private int firstSegidx; - - // dashes ref (dirty) - final ArrayCacheDouble.Reference dashes_ref; - // firstSegmentsBuffer ref (dirty) - final ArrayCacheDouble.Reference firstSegmentsBuffer_ref; - - // Bounds of the drawing region, at pixel precision. - private double[] clipRect; - - // the outcode of the current point - private int cOutCode = 0; - - private boolean subdivide = DO_CLIP_SUBDIVIDER; - - private final LengthIterator li = new LengthIterator(); - - private final CurveClipSplitter curveSplitter; - - private double cycleLen; - private boolean outside; - private double totalSkipLen; - - /** - * Constructs a DDasher. - * @param rdrCtx per-thread renderer context - */ - DDasher(final DRendererContext rdrCtx) { - this.rdrCtx = rdrCtx; - - dashes_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K - - firstSegmentsBuffer_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K - firstSegmentsBuffer = firstSegmentsBuffer_ref.initial; - - // we need curCurvepts to be able to contain 2 curves because when - // dashing curves, we need to subdivide it - curCurvepts = new double[8 * 2]; - - this.curveSplitter = rdrCtx.curveClipSplitter; - } - - /** - * Initialize the DDasher. - * - * @param out an output DPathConsumer2D. - * @param dash an array of doubles containing the dash pattern - * @param dashLen length of the given dash array - * @param phase a double containing the dash phase - * @param recycleDashes true to indicate to recycle the given dash array - * @return this instance - */ - DDasher init(final DPathConsumer2D out, final double[] dash, final int dashLen, - double phase, final boolean recycleDashes) - { - if (this.out != out) { - this.out = out; - } - - // Normalize so 0 <= phase < dash[0] - int sidx = 0; - dashOn = true; - - // note: BasicStroke constructor checks dash elements and sum > 0 - double sum = 0.0d; - for (int i = 0; i < dashLen; i++) { - sum += dash[i]; - } - this.cycleLen = sum; - - double cycles = phase / sum; - if (phase < 0.0d) { - if (-cycles >= MAX_CYCLES) { - phase = 0.0d; - } else { - int fullcycles = FloatMath.floor_int(-cycles); - if ((fullcycles & dashLen & 1) != 0) { - dashOn = !dashOn; - } - phase += fullcycles * sum; - while (phase < 0.0d) { - if (--sidx < 0) { - sidx = dashLen - 1; - } - phase += dash[sidx]; - dashOn = !dashOn; - } - } - } else if (phase > 0.0d) { - if (cycles >= MAX_CYCLES) { - phase = 0.0d; - } else { - int fullcycles = FloatMath.floor_int(cycles); - if ((fullcycles & dashLen & 1) != 0) { - dashOn = !dashOn; - } - phase -= fullcycles * sum; - double d; - while (phase >= (d = dash[sidx])) { - phase -= d; - sidx = (sidx + 1) % dashLen; - dashOn = !dashOn; - } - } - } - - this.dash = dash; - this.dashLen = dashLen; - this.phase = phase; - this.startPhase = phase; - this.startDashOn = dashOn; - this.startIdx = sidx; - this.starting = true; - this.needsMoveTo = false; - this.firstSegidx = 0; - - this.recycleDashes = recycleDashes; - - if (rdrCtx.doClip) { - this.clipRect = rdrCtx.clipRect; - } else { - this.clipRect = null; - this.cOutCode = 0; - } - return this; // fluent API - } - - /** - * Disposes this dasher: - * clean up before reusing this instance - */ - void dispose() { - if (DO_CLEAN_DIRTY) { - // Force zero-fill dirty arrays: - Arrays.fill(curCurvepts, 0.0d); - } - // Return arrays: - if (recycleDashes) { - if (dashes_ref.doCleanRef(dash)) { - dash = dashes_ref.putArray(dash); - } - } - if (firstSegmentsBuffer_ref.doCleanRef(firstSegmentsBuffer)) { - firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer); - } - } - - double[] copyDashArray(final float[] dashes) { - final int len = dashes.length; - final double[] newDashes; - if (len <= MarlinConst.INITIAL_ARRAY) { - newDashes = dashes_ref.initial; - } else { - if (DO_STATS) { - rdrCtx.stats.stat_array_dasher_dasher.add(len); - } - newDashes = dashes_ref.getArray(len); - } - for (int i = 0; i < len; i++) { newDashes[i] = dashes[i]; } - return newDashes; - } - - @Override - public void moveTo(final double x0, final double y0) { - if (firstSegidx != 0) { - out.moveTo(sx0, sy0); - emitFirstSegments(); - } - this.needsMoveTo = true; - this.idx = startIdx; - this.dashOn = this.startDashOn; - this.phase = this.startPhase; - this.cx0 = x0; - this.cy0 = y0; - - // update starting point: - this.sx0 = x0; - this.sy0 = y0; - this.starting = true; - - if (clipRect != null) { - final int outcode = DHelpers.outcode(x0, y0, clipRect); - this.cOutCode = outcode; - this.outside = false; - this.totalSkipLen = 0.0d; - } - } - - private void emitSeg(double[] buf, int off, int type) { - switch (type) { - case 4: - out.lineTo(buf[off], buf[off + 1]); - return; - case 8: - out.curveTo(buf[off ], buf[off + 1], - buf[off + 2], buf[off + 3], - buf[off + 4], buf[off + 5]); - return; - case 6: - out.quadTo(buf[off ], buf[off + 1], - buf[off + 2], buf[off + 3]); - return; - default: - } - } - - private void emitFirstSegments() { - final double[] fSegBuf = firstSegmentsBuffer; - - for (int i = 0, len = firstSegidx; i < len; ) { - int type = (int)fSegBuf[i]; - emitSeg(fSegBuf, i + 1, type); - i += (type - 1); - } - firstSegidx = 0; - } - - // precondition: pts must be in relative coordinates (relative to x0,y0) - private void goTo(final double[] pts, final int off, final int type, - final boolean on) - { - final int index = off + type; - final double x = pts[index - 4]; - final double y = pts[index - 3]; - - if (on) { - if (starting) { - goTo_starting(pts, off, type); - } else { - if (needsMoveTo) { - needsMoveTo = false; - out.moveTo(cx0, cy0); - } - emitSeg(pts, off, type); - } - } else { - if (starting) { - // low probability test (hotspot) - starting = false; - } - needsMoveTo = true; - } - this.cx0 = x; - this.cy0 = y; - } - - private void goTo_starting(final double[] pts, final int off, final int type) { - int len = type - 1; // - 2 + 1 - int segIdx = firstSegidx; - double[] buf = firstSegmentsBuffer; - - if (segIdx + len > buf.length) { - if (DO_STATS) { - rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer - .add(segIdx + len); - } - firstSegmentsBuffer = buf - = firstSegmentsBuffer_ref.widenArray(buf, segIdx, - segIdx + len); - } - buf[segIdx++] = type; - len--; - // small arraycopy (2, 4 or 6) but with offset: - System.arraycopy(pts, off, buf, segIdx, len); - firstSegidx = segIdx + len; - } - - @Override - public void lineTo(final double x1, final double y1) { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1); - - if (orCode != 0) { - final int sideCode = outcode0 & outcode1; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, - orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode1; - skipLineTo(x1, y1); - return; - } - } - - this.cOutCode = outcode1; - - if (this.outside) { - this.outside = false; - // Adjust current index, phase & dash: - skipLen(); - } - } - _lineTo(x1, y1); - } - - private void _lineTo(final double x1, final double y1) { - final double dx = x1 - cx0; - final double dy = y1 - cy0; - - double len = dx * dx + dy * dy; - if (len == 0.0d) { - return; - } - len = Math.sqrt(len); - - // The scaling factors needed to get the dx and dy of the - // transformed dash segments. - final double cx = dx / len; - final double cy = dy / len; - - final double[] _curCurvepts = curCurvepts; - final double[] _dash = dash; - final int _dashLen = this.dashLen; - - int _idx = idx; - boolean _dashOn = dashOn; - double _phase = phase; - - double leftInThisDashSegment, rem; - - while (true) { - leftInThisDashSegment = _dash[_idx] - _phase; - rem = len - leftInThisDashSegment; - - if (rem <= EPS) { - _curCurvepts[0] = x1; - _curCurvepts[1] = y1; - - goTo(_curCurvepts, 0, 4, _dashOn); - - // Advance phase within current dash segment - _phase += len; - - // compare values using epsilon: - if (Math.abs(rem) <= EPS) { - _phase = 0.0d; - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - } - break; - } - - _curCurvepts[0] = cx0 + leftInThisDashSegment * cx; - _curCurvepts[1] = cy0 + leftInThisDashSegment * cy; - - goTo(_curCurvepts, 0, 4, _dashOn); - - len = rem; - // Advance to next dash segment - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - _phase = 0.0d; - } - // Save local state: - idx = _idx; - dashOn = _dashOn; - phase = _phase; - } - - private void skipLineTo(final double x1, final double y1) { - final double dx = x1 - cx0; - final double dy = y1 - cy0; - - double len = dx * dx + dy * dy; - if (len != 0.0d) { - len = Math.sqrt(len); - } - - // Accumulate skipped length: - this.outside = true; - this.totalSkipLen += len; - - // Fix initial move: - this.needsMoveTo = true; - this.starting = false; - - this.cx0 = x1; - this.cy0 = y1; - } - - public void skipLen() { - double len = this.totalSkipLen; - this.totalSkipLen = 0.0d; - - final double[] _dash = dash; - final int _dashLen = this.dashLen; - - int _idx = idx; - boolean _dashOn = dashOn; - double _phase = phase; - - // -2 to ensure having 2 iterations of the post-loop - // to compensate the remaining phase - final long fullcycles = (long)Math.floor(len / cycleLen) - 2L; - - if (fullcycles > 0L) { - len -= cycleLen * fullcycles; - - final long iterations = fullcycles * _dashLen; - _idx = (int) (iterations + _idx) % _dashLen; - _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L; - } - - double leftInThisDashSegment, rem; - - while (true) { - leftInThisDashSegment = _dash[_idx] - _phase; - rem = len - leftInThisDashSegment; - - if (rem <= EPS) { - // Advance phase within current dash segment - _phase += len; - - // compare values using epsilon: - if (Math.abs(rem) <= EPS) { - _phase = 0.0d; - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - } - break; - } - - len = rem; - // Advance to next dash segment - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - _phase = 0.0d; - } - // Save local state: - idx = _idx; - dashOn = _dashOn; - phase = _phase; - } - - // preconditions: curCurvepts must be an array of length at least 2 * type, - // that contains the curve we want to dash in the first type elements - private void somethingTo(final int type) { - final double[] _curCurvepts = curCurvepts; - if (DHelpers.isPointCurve(_curCurvepts, type)) { - return; - } - final LengthIterator _li = li; - final double[] _dash = dash; - final int _dashLen = this.dashLen; - - _li.initializeIterationOnCurve(_curCurvepts, type); - - int _idx = idx; - boolean _dashOn = dashOn; - double _phase = phase; - - // initially the current curve is at curCurvepts[0...type] - int curCurveoff = 0; - double prevT = 0.0d; - double t; - double leftInThisDashSegment = _dash[_idx] - _phase; - - while ((t = _li.next(leftInThisDashSegment)) < 1.0d) { - if (t != 0.0d) { - DHelpers.subdivideAt((t - prevT) / (1.0d - prevT), - _curCurvepts, curCurveoff, - _curCurvepts, 0, type); - prevT = t; - goTo(_curCurvepts, 2, type, _dashOn); - curCurveoff = type; - } - // Advance to next dash segment - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - _phase = 0.0d; - leftInThisDashSegment = _dash[_idx]; - } - - goTo(_curCurvepts, curCurveoff + 2, type, _dashOn); - - _phase += _li.lastSegLen(); - - // compare values using epsilon: - if (_phase + EPS >= _dash[_idx]) { - _phase = 0.0d; - _idx = (_idx + 1) % _dashLen; - _dashOn = !_dashOn; - } - // Save local state: - idx = _idx; - dashOn = _dashOn; - phase = _phase; - - // reset LengthIterator: - _li.reset(); - } - - private void skipSomethingTo(final int type) { - final double[] _curCurvepts = curCurvepts; - if (DHelpers.isPointCurve(_curCurvepts, type)) { - return; - } - final LengthIterator _li = li; - - _li.initializeIterationOnCurve(_curCurvepts, type); - - // In contrary to somethingTo(), - // just estimate properly the curve length: - final double len = _li.totalLength(); - - // Accumulate skipped length: - this.outside = true; - this.totalSkipLen += len; - - // Fix initial move: - this.needsMoveTo = true; - this.starting = false; - } - - // Objects of this class are used to iterate through curves. They return - // t values where the left side of the curve has a specified length. - // It does this by subdividing the input curve until a certain error - // condition has been met. A recursive subdivision procedure would - // return as many as 1<= 0; i--) { - Arrays.fill(recCurveStack[i], 0.0d); - } - Arrays.fill(sidesRight, false); - Arrays.fill(curLeafCtrlPolyLengths, 0.0d); - Arrays.fill(nextRoots, 0.0d); - Arrays.fill(flatLeafCoefCache, 0.0d); - flatLeafCoefCache[2] = -1.0d; - } - } - - void initializeIterationOnCurve(final double[] pts, final int type) { - // optimize arraycopy (8 values faster than 6 = type): - System.arraycopy(pts, 0, recCurveStack[0], 0, 8); - this.curveType = type; - this.recLevel = 0; - this.lastT = 0.0d; - this.lenAtLastT = 0.0d; - this.nextT = 0.0d; - this.lenAtNextT = 0.0d; - // initializes nextT and lenAtNextT properly - goLeft(); - - this.lenAtLastSplit = 0.0d; - if (recLevel > 0) { - this.sidesRight[0] = false; - this.done = false; - } else { - // the root of the tree is a leaf so we're done. - this.sidesRight[0] = true; - this.done = true; - } - this.lastSegLen = 0.0d; - } - - // 0 == false, 1 == true, -1 == invalid cached value. - private int cachedHaveLowAcceleration = -1; - - private boolean haveLowAcceleration(final double err) { - if (cachedHaveLowAcceleration == -1) { - final double len1 = curLeafCtrlPolyLengths[0]; - final double len2 = curLeafCtrlPolyLengths[1]; - // the test below is equivalent to !within(len1/len2, 1, err). - // It is using a multiplication instead of a division, so it - // should be a bit faster. - if (!DHelpers.within(len1, len2, err * len2)) { - cachedHaveLowAcceleration = 0; - return false; - } - if (curveType == 8) { - final double len3 = curLeafCtrlPolyLengths[2]; - // if len1 is close to 2 and 2 is close to 3, that probably - // means 1 is close to 3 so the second part of this test might - // not be needed, but it doesn't hurt to include it. - final double errLen3 = err * len3; - if (!(DHelpers.within(len2, len3, errLen3) && - DHelpers.within(len1, len3, errLen3))) { - cachedHaveLowAcceleration = 0; - return false; - } - } - cachedHaveLowAcceleration = 1; - return true; - } - - return (cachedHaveLowAcceleration == 1); - } - - // we want to avoid allocations/gc so we keep this array so we - // can put roots in it, - private final double[] nextRoots = new double[4]; - - // caches the coefficients of the current leaf in its flattened - // form (see inside next() for what that means). The cache is - // invalid when it's third element is negative, since in any - // valid flattened curve, this would be >= 0. - private final double[] flatLeafCoefCache = new double[]{0.0d, 0.0d, -1.0d, 0.0d}; - - // returns the t value where the remaining curve should be split in - // order for the left subdivided curve to have length len. If len - // is >= than the length of the uniterated curve, it returns 1. - double next(final double len) { - final double targetLength = lenAtLastSplit + len; - while (lenAtNextT < targetLength) { - if (done) { - lastSegLen = lenAtNextT - lenAtLastSplit; - return 1.0d; - } - goToNextLeaf(); - } - lenAtLastSplit = targetLength; - final double leaflen = lenAtNextT - lenAtLastT; - double t = (targetLength - lenAtLastT) / leaflen; - - // cubicRootsInAB is a fairly expensive call, so we just don't do it - // if the acceleration in this section of the curve is small enough. - if (!haveLowAcceleration(0.05d)) { - // We flatten the current leaf along the x axis, so that we're - // left with a, b, c which define a 1D Bezier curve. We then - // solve this to get the parameter of the original leaf that - // gives us the desired length. - final double[] _flatLeafCoefCache = flatLeafCoefCache; - - if (_flatLeafCoefCache[2] < 0.0d) { - double x = curLeafCtrlPolyLengths[0], - y = x + curLeafCtrlPolyLengths[1]; - if (curveType == 8) { - double z = y + curLeafCtrlPolyLengths[2]; - _flatLeafCoefCache[0] = 3.0d * (x - y) + z; - _flatLeafCoefCache[1] = 3.0d * (y - 2.0d * x); - _flatLeafCoefCache[2] = 3.0d * x; - _flatLeafCoefCache[3] = -z; - } else if (curveType == 6) { - _flatLeafCoefCache[0] = 0.0d; - _flatLeafCoefCache[1] = y - 2.0d * x; - _flatLeafCoefCache[2] = 2.0d * x; - _flatLeafCoefCache[3] = -y; - } - } - double a = _flatLeafCoefCache[0]; - double b = _flatLeafCoefCache[1]; - double c = _flatLeafCoefCache[2]; - double d = t * _flatLeafCoefCache[3]; - - // we use cubicRootsInAB here, because we want only roots in 0, 1, - // and our quadratic root finder doesn't filter, so it's just a - // matter of convenience. - final int n = DHelpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d); - if (n == 1 && !Double.isNaN(nextRoots[0])) { - t = nextRoots[0]; - } - } - // t is relative to the current leaf, so we must make it a valid parameter - // of the original curve. - t = t * (nextT - lastT) + lastT; - if (t >= 1.0d) { - t = 1.0d; - done = true; - } - // even if done = true, if we're here, that means targetLength - // is equal to, or very, very close to the total length of the - // curve, so lastSegLen won't be too high. In cases where len - // overshoots the curve, this method will exit in the while - // loop, and lastSegLen will still be set to the right value. - lastSegLen = len; - return t; - } - - double totalLength() { - while (!done) { - goToNextLeaf(); - } - // reset LengthIterator: - reset(); - - return lenAtNextT; - } - - double lastSegLen() { - return lastSegLen; - } - - // go to the next leaf (in an inorder traversal) in the recursion tree - // preconditions: must be on a leaf, and that leaf must not be the root. - private void goToNextLeaf() { - // We must go to the first ancestor node that has an unvisited - // right child. - final boolean[] _sides = sidesRight; - int _recLevel = recLevel; - _recLevel--; - - while(_sides[_recLevel]) { - if (_recLevel == 0) { - recLevel = 0; - done = true; - return; - } - _recLevel--; - } - - _sides[_recLevel] = true; - // optimize arraycopy (8 values faster than 6 = type): - System.arraycopy(recCurveStack[_recLevel++], 0, - recCurveStack[_recLevel], 0, 8); - recLevel = _recLevel; - goLeft(); - } - - // go to the leftmost node from the current node. Return its length. - private void goLeft() { - final double len = onLeaf(); - if (len >= 0.0d) { - lastT = nextT; - lenAtLastT = lenAtNextT; - nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC; - lenAtNextT += len; - // invalidate caches - flatLeafCoefCache[2] = -1.0d; - cachedHaveLowAcceleration = -1; - } else { - DHelpers.subdivide(recCurveStack[recLevel], - recCurveStack[recLevel + 1], - recCurveStack[recLevel], curveType); - - sidesRight[recLevel] = false; - recLevel++; - goLeft(); - } - } - - // this is a bit of a hack. It returns -1 if we're not on a leaf, and - // the length of the leaf if we are on a leaf. - private double onLeaf() { - final double[] curve = recCurveStack[recLevel]; - final int _curveType = curveType; - double polyLen = 0.0d; - - double x0 = curve[0], y0 = curve[1]; - for (int i = 2; i < _curveType; i += 2) { - final double x1 = curve[i], y1 = curve[i + 1]; - final double len = DHelpers.linelen(x0, y0, x1, y1); - polyLen += len; - curLeafCtrlPolyLengths[(i >> 1) - 1] = len; - x0 = x1; - y0 = y1; - } - - final double lineLen = DHelpers.linelen(curve[0], curve[1], x0, y0); - - if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) { - return (polyLen + lineLen) / 2.0d; - } - return -1.0d; - } - } - - @Override - public void curveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(x2, y2, clipRect); - final int outcode3 = DHelpers.outcode(x3, y3, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2 | outcode3); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3, - orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode3; - skipCurveTo(x1, y1, x2, y2, x3, y3); - return; - } - } - - this.cOutCode = outcode3; - - if (this.outside) { - this.outside = false; - // Adjust current index, phase & dash: - skipLen(); - } - } - _curveTo(x1, y1, x2, y2, x3, y3); - } - - private void _curveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final double[] _curCurvepts = curCurvepts; - - // monotonize curve: - final CurveBasicMonotonizer monotonizer - = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3); - - final int nSplits = monotonizer.nbSplits; - final double[] mid = monotonizer.middle; - - // Implicitely rdrCtx.isFirstSegment = true - - for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { - // optimize arraycopy (8 values faster than 6 = type): - System.arraycopy(mid, off, _curCurvepts, 0, 8); - - somethingTo(8); - - // set flag rdrCtx.isFirstSegment = false for other parts: - rdrCtx.isFirstSegment = false; // TODO: handle conflict with clipper - } - // reset trigger to process further joins (normal operations) - rdrCtx.isFirstSegment = true; - } - - private void skipCurveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final double[] _curCurvepts = curCurvepts; - _curCurvepts[0] = cx0; _curCurvepts[1] = cy0; - _curCurvepts[2] = x1; _curCurvepts[3] = y1; - _curCurvepts[4] = x2; _curCurvepts[5] = y2; - _curCurvepts[6] = x3; _curCurvepts[7] = y3; - - skipSomethingTo(8); - - this.cx0 = x3; - this.cy0 = y3; - } - - @Override - public void quadTo(final double x1, final double y1, - final double x2, final double y2) - { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(x2, y2, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => call lineTo() with subdivided curves: - boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, - x2, y2, orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode2; - skipQuadTo(x1, y1, x2, y2); - return; - } - } - - this.cOutCode = outcode2; - - if (this.outside) { - this.outside = false; - // Adjust current index, phase & dash: - skipLen(); - } - } - _quadTo(x1, y1, x2, y2); - } - - private void _quadTo(final double x1, final double y1, - final double x2, final double y2) - { - final double[] _curCurvepts = curCurvepts; - - // monotonize quad: - final CurveBasicMonotonizer monotonizer - = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2); - - final int nSplits = monotonizer.nbSplits; - final double[] mid = monotonizer.middle; - - // Implicitely rdrCtx.isFirstSegment = true - - for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { - // optimize arraycopy (8 values faster than 6 = type): - System.arraycopy(mid, off, _curCurvepts, 0, 8); - - somethingTo(6); - - // set flag rdrCtx.isFirstSegment = false for other parts: - rdrCtx.isFirstSegment = false; // TODO: handle conflict with clipper - } - // reset trigger to process further joins (normal operations) - rdrCtx.isFirstSegment = true; - } - - private void skipQuadTo(final double x1, final double y1, - final double x2, final double y2) - { - final double[] _curCurvepts = curCurvepts; - _curCurvepts[0] = cx0; _curCurvepts[1] = cy0; - _curCurvepts[2] = x1; _curCurvepts[3] = y1; - _curCurvepts[4] = x2; _curCurvepts[5] = y2; - - skipSomethingTo(6); - - this.cx0 = x2; - this.cy0 = y2; - } - - @Override - public void closePath() { - if (cx0 != sx0 || cy0 != sy0) { - lineTo(sx0, sy0); - } - if (firstSegidx != 0) { - if (!dashOn || needsMoveTo) { - out.moveTo(sx0, sy0); - } - emitFirstSegments(); - } - moveTo(sx0, sy0); - } - - @Override - public void pathDone() { - if (firstSegidx != 0) { - out.moveTo(sx0, sy0); - emitFirstSegments(); - } - out.pathDone(); - - // Dispose this instance: - dispose(); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("DDasher does not use a native consumer"); - } -} - diff --git a/src/main/java/sun/java2d/marlin/DHelpers.java b/src/main/java/sun/java2d/marlin/DHelpers.java deleted file mode 100644 index 9a01cfb..0000000 --- a/src/main/java/sun/java2d/marlin/DHelpers.java +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.util.Arrays; -import net.jafama.FastMath; -import sun.java2d.marlin.stats.Histogram; -import sun.java2d.marlin.stats.StatLong; - -final class DHelpers implements MarlinConst { - - private static final double EPS = 1e-9d; - - private DHelpers() { - throw new Error("This is a non instantiable class"); - } - - static boolean within(final double x, final double y) { - return within(x, y, EPS); - } - - static boolean within(final double x, final double y, final double err) { - return withinD(y - x, err); - } - - static boolean withinD(final double d, final double err) { - return (d <= err && d >= -err); - } - - static boolean withinD(final double dx, final double dy, final double err) - { - assert err > 0 : ""; - // compare taxicab distance. ERR will always be small, so using - // true distance won't give much benefit - return (withinD(dx, err) && // we want to avoid calling Math.abs - withinD(dy, err)); // this is just as good. - } - - static boolean isPointCurve(final double[] curve, final int type) { - return isPointCurve(curve, type, EPS); - } - - static boolean isPointCurve(final double[] curve, final int type, final double err) { - for (int i = 2; i < type; i++) { - if (!within(curve[i], curve[i - 2], err)) { - return false; - } - } - return true; - } - - static double evalCubic(final double a, final double b, - final double c, final double d, - final double t) - { - return t * (t * (t * a + b) + c) + d; - } - - static double evalQuad(final double a, final double b, - final double c, final double t) - { - return t * (t * a + b) + c; - } - - static int quadraticRoots(final double a, final double b, final double c, - final double[] zeroes, final int off) - { - int ret = off; - if (a != 0.0d) { - double d = b * b - 4.0d * a * c; - if (d > 0.0d) { - d = Math.sqrt(d); - // For accuracy, calculate one root using: - // (-b +/- d) / 2a - // and the other using: - // 2c / (-b +/- d) - // Choose the sign of the +/- so that b+d gets larger in magnitude - if (b < 0.0d) { - d = -d; - } - final double q = (b + d) / -2.0d; - // We already tested a for being 0 above - zeroes[ret++] = q / a; - if (q != 0.0d) { - zeroes[ret++] = c / q; - } - } else if (d == 0.0d) { - zeroes[ret++] = -b / (2.0d * a); - } - } else if (b != 0.0d) { - zeroes[ret++] = -c / b; - } - return ret - off; - } - - // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B) - static int cubicRootsInAB(final double d, double a, double b, double c, - final double[] pts, final int off, - final double A, final double B) - { - if (d == 0.0d) { - final int num = quadraticRoots(a, b, c, pts, off); - return filterOutNotInAB(pts, off, num, A, B) - off; - } - // From Graphics Gems: - // https://github.com/erich666/GraphicsGems/blob/master/gems/Roots3And4.c - // (also from awt.geom.CubicCurve2D. But here we don't need as - // much accuracy and we don't want to create arrays so we use - // our own customized version). - - // normal form: x^3 + ax^2 + bx + c = 0 - a /= d; - b /= d; - c /= d; - - // substitute x = y - A/3 to eliminate quadratic term: - // x^3 +Px + Q = 0 - // - // Since we actually need P/3 and Q/2 for all of the - // calculations that follow, we will calculate - // p = P/3 - // q = Q/2 - // instead and use those values for simplicity of the code. - final double sq_A = a * a; - final double p = (1.0d / 3.0d) * ((-1.0d / 3.0d) * sq_A + b); - final double sub = (1.0d / 3.0d) * a; - final double q = (1.0d / 2.0d) * ((2.0d / 27.0d) * a * sq_A - sub * b + c); - - // use Cardano's formula - final double cb_p = p * p * p; - final double D = q * q + cb_p; - - int num; - - if (within(D, 0.0d)) { - if (within(q, 0.0d)) { - /* one triple solution */ - pts[off ] = (- sub); - num = 1; - } else { - /* one single and one double solution */ - final double u = FastMath.cbrt(-q); - pts[off ] = (2.0d * u - sub); - pts[off + 1] = (- u - sub); - num = 2; - } - } else if (D < 0.0d) { - // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method - final double phi = (1.0d / 3.0d) * FastMath.acos(-q / Math.sqrt(-cb_p)); - final double t = 2.0d * Math.sqrt(-p); - - pts[off ] = ( t * FastMath.cos(phi) - sub); - pts[off + 1] = (-t * FastMath.cos(phi + (Math.PI / 3.0d)) - sub); - pts[off + 2] = (-t * FastMath.cos(phi - (Math.PI / 3.0d)) - sub); - num = 3; - } else { - final double sqrt_D = Math.sqrt(D); - final double u = FastMath.cbrt(sqrt_D - q); - final double v = - FastMath.cbrt(sqrt_D + q); - - pts[off ] = (u + v - sub); - num = 1; - } - return filterOutNotInAB(pts, off, num, A, B) - off; - } - - // returns the index 1 past the last valid element remaining after filtering - static int filterOutNotInAB(final double[] nums, final int off, final int len, - final double a, final double b) - { - int ret = off; - for (int i = off, end = off + len; i < end; i++) { - if (nums[i] >= a && nums[i] < b) { - nums[ret++] = nums[i]; - } - } - return ret; - } - - static double fastLineLen(final double x0, final double y0, - final double x1, final double y1) - { - final double dx = x1 - x0; - final double dy = y1 - y0; - - // use manhattan norm: - return Math.abs(dx) + Math.abs(dy); - } - - static double linelen(final double x0, final double y0, - final double x1, final double y1) - { - final double dx = x1 - x0; - final double dy = y1 - y0; - return Math.sqrt(dx * dx + dy * dy); - } - - static double fastQuadLen(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2) - { - final double dx1 = x1 - x0; - final double dx2 = x2 - x1; - final double dy1 = y1 - y0; - final double dy2 = y2 - y1; - - // use manhattan norm: - return Math.abs(dx1) + Math.abs(dx2) - + Math.abs(dy1) + Math.abs(dy2); - } - - static double quadlen(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2) - { - return (linelen(x0, y0, x1, y1) - + linelen(x1, y1, x2, y2) - + linelen(x0, y0, x2, y2)) / 2.0d; - } - - static double fastCurvelen(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final double dx1 = x1 - x0; - final double dx2 = x2 - x1; - final double dx3 = x3 - x2; - final double dy1 = y1 - y0; - final double dy2 = y2 - y1; - final double dy3 = y3 - y2; - - // use manhattan norm: - return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3) - + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3); - } - - static double curvelen(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - return (linelen(x0, y0, x1, y1) - + linelen(x1, y1, x2, y2) - + linelen(x2, y2, x3, y3) - + linelen(x0, y0, x3, y3)) / 2.0d; - } - - // finds values of t where the curve in pts should be subdivided in order - // to get good offset curves a distance of w away from the middle curve. - // Stores the points in ts, and returns how many of them there were. - static int findSubdivPoints(final DCurve c, final double[] pts, - final double[] ts, final int type, - final double w2) - { - final double x12 = pts[2] - pts[0]; - final double y12 = pts[3] - pts[1]; - // if the curve is already parallel to either axis we gain nothing - // from rotating it. - if ((y12 != 0.0d) && (x12 != 0.0d)) { - // we rotate it so that the first vector in the control polygon is - // parallel to the x-axis. This will ensure that rotated quarter - // circles won't be subdivided. - final double hypot = Math.sqrt(x12 * x12 + y12 * y12); - final double cos = x12 / hypot; - final double sin = y12 / hypot; - final double x1 = cos * pts[0] + sin * pts[1]; - final double y1 = cos * pts[1] - sin * pts[0]; - final double x2 = cos * pts[2] + sin * pts[3]; - final double y2 = cos * pts[3] - sin * pts[2]; - final double x3 = cos * pts[4] + sin * pts[5]; - final double y3 = cos * pts[5] - sin * pts[4]; - - switch(type) { - case 8: - final double x4 = cos * pts[6] + sin * pts[7]; - final double y4 = cos * pts[7] - sin * pts[6]; - c.set(x1, y1, x2, y2, x3, y3, x4, y4); - break; - case 6: - c.set(x1, y1, x2, y2, x3, y3); - break; - default: - } - } else { - c.set(pts, type); - } - - int ret = 0; - // we subdivide at values of t such that the remaining rotated - // curves are monotonic in x and y. - ret += c.dxRoots(ts, ret); - ret += c.dyRoots(ts, ret); - - // subdivide at inflection points. - if (type == 8) { - // quadratic curves can't have inflection points - ret += c.infPoints(ts, ret); - } - - // now we must subdivide at points where one of the offset curves will have - // a cusp. This happens at ts where the radius of curvature is equal to w. - ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d); - - ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d); - isort(ts, ret); - return ret; - } - - // finds values of t where the curve in pts should be subdivided in order - // to get intersections with the given clip rectangle. - // Stores the points in ts, and returns how many of them there were. - static int findClipPoints(final DCurve curve, final double[] pts, - final double[] ts, final int type, - final int outCodeOR, - final double[] clipRect) - { - curve.set(pts, type); - - // clip rectangle (ymin, ymax, xmin, xmax) - int ret = 0; - - if ((outCodeOR & OUTCODE_LEFT) != 0) { - ret += curve.xPoints(ts, ret, clipRect[2]); - } - if ((outCodeOR & OUTCODE_RIGHT) != 0) { - ret += curve.xPoints(ts, ret, clipRect[3]); - } - if ((outCodeOR & OUTCODE_TOP) != 0) { - ret += curve.yPoints(ts, ret, clipRect[0]); - } - if ((outCodeOR & OUTCODE_BOTTOM) != 0) { - ret += curve.yPoints(ts, ret, clipRect[1]); - } - isort(ts, ret); - return ret; - } - - static void subdivide(final double[] src, - final double[] left, final double[] right, - final int type) - { - switch(type) { - case 8: - subdivideCubic(src, left, right); - return; - case 6: - subdivideQuad(src, left, right); - return; - default: - throw new InternalError("Unsupported curve type"); - } - } - - static void isort(final double[] a, final int len) { - for (int i = 1, j; i < len; i++) { - final double ai = a[i]; - j = i - 1; - for (; j >= 0 && a[j] > ai; j--) { - a[j + 1] = a[j]; - } - a[j + 1] = ai; - } - } - - // Most of these are copied from classes in java.awt.geom because we need - // both single and double precision variants of these functions, and Line2D, - // CubicCurve2D, QuadCurve2D don't provide them. - /** - * Subdivides the cubic curve specified by the coordinates - * stored in the src array at indices srcoff - * through (srcoff + 7) and stores the - * resulting two subdivided curves into the two result arrays at the - * corresponding indices. - * Either or both of the left and right - * arrays may be null or a reference to the same array - * as the src array. - * Note that the last point in the first subdivided curve is the - * same as the first point in the second subdivided curve. Thus, - * it is possible to pass the same array for left - * and right and to use offsets, such as rightoff - * equals (leftoff + 6), in order - * to avoid allocating extra storage for this common point. - * @param src the array holding the coordinates for the source curve - * @param left the array for storing the coordinates for the first - * half of the subdivided curve - * @param right the array for storing the coordinates for the second - * half of the subdivided curve - * @since 1.7 - */ - static void subdivideCubic(final double[] src, - final double[] left, - final double[] right) - { - double x1 = src[0]; - double y1 = src[1]; - double cx1 = src[2]; - double cy1 = src[3]; - double cx2 = src[4]; - double cy2 = src[5]; - double x2 = src[6]; - double y2 = src[7]; - - left[0] = x1; - left[1] = y1; - - right[6] = x2; - right[7] = y2; - - x1 = (x1 + cx1) / 2.0d; - y1 = (y1 + cy1) / 2.0d; - x2 = (x2 + cx2) / 2.0d; - y2 = (y2 + cy2) / 2.0d; - - double cx = (cx1 + cx2) / 2.0d; - double cy = (cy1 + cy2) / 2.0d; - - cx1 = (x1 + cx) / 2.0d; - cy1 = (y1 + cy) / 2.0d; - cx2 = (x2 + cx) / 2.0d; - cy2 = (y2 + cy) / 2.0d; - cx = (cx1 + cx2) / 2.0d; - cy = (cy1 + cy2) / 2.0d; - - left[2] = x1; - left[3] = y1; - left[4] = cx1; - left[5] = cy1; - left[6] = cx; - left[7] = cy; - - right[0] = cx; - right[1] = cy; - right[2] = cx2; - right[3] = cy2; - right[4] = x2; - right[5] = y2; - } - - static void subdivideCubicAt(final double t, - final double[] src, final int offS, - final double[] pts, final int offL, final int offR) - { - double x1 = src[offS ]; - double y1 = src[offS + 1]; - double cx1 = src[offS + 2]; - double cy1 = src[offS + 3]; - double cx2 = src[offS + 4]; - double cy2 = src[offS + 5]; - double x2 = src[offS + 6]; - double y2 = src[offS + 7]; - - pts[offL ] = x1; - pts[offL + 1] = y1; - - pts[offR + 6] = x2; - pts[offR + 7] = y2; - - x1 = x1 + t * (cx1 - x1); - y1 = y1 + t * (cy1 - y1); - x2 = cx2 + t * (x2 - cx2); - y2 = cy2 + t * (y2 - cy2); - - double cx = cx1 + t * (cx2 - cx1); - double cy = cy1 + t * (cy2 - cy1); - - cx1 = x1 + t * (cx - x1); - cy1 = y1 + t * (cy - y1); - cx2 = cx + t * (x2 - cx); - cy2 = cy + t * (y2 - cy); - cx = cx1 + t * (cx2 - cx1); - cy = cy1 + t * (cy2 - cy1); - - pts[offL + 2] = x1; - pts[offL + 3] = y1; - pts[offL + 4] = cx1; - pts[offL + 5] = cy1; - pts[offL + 6] = cx; - pts[offL + 7] = cy; - - pts[offR ] = cx; - pts[offR + 1] = cy; - pts[offR + 2] = cx2; - pts[offR + 3] = cy2; - pts[offR + 4] = x2; - pts[offR + 5] = y2; - } - - static void subdivideQuad(final double[] src, - final double[] left, - final double[] right) - { - double x1 = src[0]; - double y1 = src[1]; - double cx = src[2]; - double cy = src[3]; - double x2 = src[4]; - double y2 = src[5]; - - left[0] = x1; - left[1] = y1; - - right[4] = x2; - right[5] = y2; - - x1 = (x1 + cx) / 2.0d; - y1 = (y1 + cy) / 2.0d; - x2 = (x2 + cx) / 2.0d; - y2 = (y2 + cy) / 2.0d; - cx = (x1 + x2) / 2.0d; - cy = (y1 + y2) / 2.0d; - - left[2] = x1; - left[3] = y1; - left[4] = cx; - left[5] = cy; - - right[0] = cx; - right[1] = cy; - right[2] = x2; - right[3] = y2; - } - - static void subdivideQuadAt(final double t, - final double[] src, final int offS, - final double[] pts, final int offL, final int offR) - { - double x1 = src[offS ]; - double y1 = src[offS + 1]; - double cx = src[offS + 2]; - double cy = src[offS + 3]; - double x2 = src[offS + 4]; - double y2 = src[offS + 5]; - - pts[offL ] = x1; - pts[offL + 1] = y1; - - pts[offR + 4] = x2; - pts[offR + 5] = y2; - - x1 = x1 + t * (cx - x1); - y1 = y1 + t * (cy - y1); - x2 = cx + t * (x2 - cx); - y2 = cy + t * (y2 - cy); - cx = x1 + t * (x2 - x1); - cy = y1 + t * (y2 - y1); - - pts[offL + 2] = x1; - pts[offL + 3] = y1; - pts[offL + 4] = cx; - pts[offL + 5] = cy; - - pts[offR ] = cx; - pts[offR + 1] = cy; - pts[offR + 2] = x2; - pts[offR + 3] = y2; - } - - static void subdivideLineAt(final double t, - final double[] src, final int offS, - final double[] pts, final int offL, final int offR) - { - double x1 = src[offS ]; - double y1 = src[offS + 1]; - double x2 = src[offS + 2]; - double y2 = src[offS + 3]; - - pts[offL ] = x1; - pts[offL + 1] = y1; - - pts[offR + 2] = x2; - pts[offR + 3] = y2; - - x1 = x1 + t * (x2 - x1); - y1 = y1 + t * (y2 - y1); - - pts[offL + 2] = x1; - pts[offL + 3] = y1; - - pts[offR ] = x1; - pts[offR + 1] = y1; - } - - static void subdivideAt(final double t, - final double[] src, final int offS, - final double[] pts, final int offL, final int type) - { - // if instead of switch (perf + most probable cases first) - if (type == 8) { - subdivideCubicAt(t, src, offS, pts, offL, offL + type); - } else if (type == 4) { - subdivideLineAt(t, src, offS, pts, offL, offL + type); - } else { - subdivideQuadAt(t, src, offS, pts, offL, offL + type); - } - } - - // From sun.java2d.loops.GeneralRenderer: - - static int outcode(final double x, final double y, - final double[] clipRect) - { - int code; - if (y < clipRect[0]) { - code = OUTCODE_TOP; - } else if (y >= clipRect[1]) { - code = OUTCODE_BOTTOM; - } else { - code = 0; - } - if (x < clipRect[2]) { - code |= OUTCODE_LEFT; - } else if (x >= clipRect[3]) { - code |= OUTCODE_RIGHT; - } - return code; - } - - // a stack of polynomial curves where each curve shares endpoints with - // adjacent ones. - static final class PolyStack { - private static final byte TYPE_LINETO = (byte) 0; - private static final byte TYPE_QUADTO = (byte) 1; - private static final byte TYPE_CUBICTO = (byte) 2; - - // curves capacity = edges count (8192) = edges x 2 (coords) - private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1; - - // types capacity = edges count (4096) - private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT; - - double[] curves; - int end; - byte[] curveTypes; - int numCurves; - - // curves ref (dirty) - final ArrayCacheDouble.Reference curves_ref; - // curveTypes ref (dirty) - final ArrayCacheByte.Reference curveTypes_ref; - - // used marks (stats only) - int curveTypesUseMark; - int curvesUseMark; - - private final StatLong stat_polystack_types; - private final StatLong stat_polystack_curves; - private final Histogram hist_polystack_curves; - private final StatLong stat_array_polystack_curves; - private final StatLong stat_array_polystack_curveTypes; - - PolyStack(final DRendererContext rdrCtx) { - this(rdrCtx, null, null, null, null, null); - } - - PolyStack(final DRendererContext rdrCtx, - final StatLong stat_polystack_types, - final StatLong stat_polystack_curves, - final Histogram hist_polystack_curves, - final StatLong stat_array_polystack_curves, - final StatLong stat_array_polystack_curveTypes) - { - curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K - curves = curves_ref.initial; - - curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K - curveTypes = curveTypes_ref.initial; - numCurves = 0; - end = 0; - - if (DO_STATS) { - curveTypesUseMark = 0; - curvesUseMark = 0; - } - this.stat_polystack_types = stat_polystack_types; - this.stat_polystack_curves = stat_polystack_curves; - this.hist_polystack_curves = hist_polystack_curves; - this.stat_array_polystack_curves = stat_array_polystack_curves; - this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes; - } - - /** - * Disposes this PolyStack: - * clean up before reusing this instance - */ - void dispose() { - end = 0; - numCurves = 0; - - if (DO_STATS) { - stat_polystack_types.add(curveTypesUseMark); - stat_polystack_curves.add(curvesUseMark); - hist_polystack_curves.add(curvesUseMark); - - // reset marks - curveTypesUseMark = 0; - curvesUseMark = 0; - } - - // Return arrays: - // curves and curveTypes are kept dirty - if (curves_ref.doCleanRef(curves)) { - curves = curves_ref.putArray(curves); - } - if (curveTypes_ref.doCleanRef(curveTypes)) { - curveTypes = curveTypes_ref.putArray(curveTypes); - } - } - - private void ensureSpace(final int n) { - // use substraction to avoid integer overflow: - if (curves.length - end < n) { - if (DO_STATS) { - stat_array_polystack_curves.add(end + n); - } - curves = curves_ref.widenArray(curves, end, end + n); - } - if (curveTypes.length <= numCurves) { - if (DO_STATS) { - stat_array_polystack_curveTypes.add(numCurves + 1); - } - curveTypes = curveTypes_ref.widenArray(curveTypes, - numCurves, - numCurves + 1); - } - } - - void pushCubic(double x0, double y0, - double x1, double y1, - double x2, double y2) - { - ensureSpace(6); - curveTypes[numCurves++] = TYPE_CUBICTO; - // we reverse the coordinate order to make popping easier - final double[] _curves = curves; - int e = end; - _curves[e++] = x2; _curves[e++] = y2; - _curves[e++] = x1; _curves[e++] = y1; - _curves[e++] = x0; _curves[e++] = y0; - end = e; - } - - void pushQuad(double x0, double y0, - double x1, double y1) - { - ensureSpace(4); - curveTypes[numCurves++] = TYPE_QUADTO; - final double[] _curves = curves; - int e = end; - _curves[e++] = x1; _curves[e++] = y1; - _curves[e++] = x0; _curves[e++] = y0; - end = e; - } - - void pushLine(double x, double y) { - ensureSpace(2); - curveTypes[numCurves++] = TYPE_LINETO; - curves[end++] = x; curves[end++] = y; - } - - void pullAll(final DPathConsumer2D io) { - final int nc = numCurves; - if (nc == 0) { - return; - } - if (DO_STATS) { - // update used marks: - if (numCurves > curveTypesUseMark) { - curveTypesUseMark = numCurves; - } - if (end > curvesUseMark) { - curvesUseMark = end; - } - } - final byte[] _curveTypes = curveTypes; - final double[] _curves = curves; - int e = 0; - - for (int i = 0; i < nc; i++) { - switch(_curveTypes[i]) { - case TYPE_LINETO: - io.lineTo(_curves[e], _curves[e+1]); - e += 2; - continue; - case TYPE_CUBICTO: - io.curveTo(_curves[e], _curves[e+1], - _curves[e+2], _curves[e+3], - _curves[e+4], _curves[e+5]); - e += 6; - continue; - case TYPE_QUADTO: - io.quadTo(_curves[e], _curves[e+1], - _curves[e+2], _curves[e+3]); - e += 4; - continue; - default: - } - } - numCurves = 0; - end = 0; - } - - void popAll(final DPathConsumer2D io) { - int nc = numCurves; - if (nc == 0) { - return; - } - if (DO_STATS) { - // update used marks: - if (numCurves > curveTypesUseMark) { - curveTypesUseMark = numCurves; - } - if (end > curvesUseMark) { - curvesUseMark = end; - } - } - final byte[] _curveTypes = curveTypes; - final double[] _curves = curves; - int e = end; - - while (nc != 0) { - switch(_curveTypes[--nc]) { - case TYPE_LINETO: - e -= 2; - io.lineTo(_curves[e], _curves[e+1]); - continue; - case TYPE_CUBICTO: - e -= 6; - io.curveTo(_curves[e], _curves[e+1], - _curves[e+2], _curves[e+3], - _curves[e+4], _curves[e+5]); - continue; - case TYPE_QUADTO: - e -= 4; - io.quadTo(_curves[e], _curves[e+1], - _curves[e+2], _curves[e+3]); - continue; - default: - } - } - numCurves = 0; - end = 0; - } - - @Override - public String toString() { - String ret = ""; - int nc = numCurves; - int last = end; - int len; - while (nc != 0) { - switch(curveTypes[--nc]) { - case TYPE_LINETO: - len = 2; - ret += "line: "; - break; - case TYPE_QUADTO: - len = 4; - ret += "quad: "; - break; - case TYPE_CUBICTO: - len = 6; - ret += "cubic: "; - break; - default: - len = 0; - } - last -= len; - ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len)) - + "\n"; - } - return ret; - } - } - - // a stack of integer indices - static final class IndexStack { - - // integer capacity = edges count / 4 ~ 1024 - private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2; - - private int end; - private int[] indices; - - // indices ref (dirty) - private final ArrayCacheInt.Reference indices_ref; - - // used marks (stats only) - private int indicesUseMark; - - private final StatLong stat_idxstack_indices; - private final Histogram hist_idxstack_indices; - private final StatLong stat_array_idxstack_indices; - - IndexStack(final DRendererContext rdrCtx) { - this(rdrCtx, null, null, null); - } - - IndexStack(final DRendererContext rdrCtx, - final StatLong stat_idxstack_indices, - final Histogram hist_idxstack_indices, - final StatLong stat_array_idxstack_indices) - { - indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K - indices = indices_ref.initial; - end = 0; - - if (DO_STATS) { - indicesUseMark = 0; - } - this.stat_idxstack_indices = stat_idxstack_indices; - this.hist_idxstack_indices = hist_idxstack_indices; - this.stat_array_idxstack_indices = stat_array_idxstack_indices; - } - - /** - * Disposes this PolyStack: - * clean up before reusing this instance - */ - void dispose() { - end = 0; - - if (DO_STATS) { - stat_idxstack_indices.add(indicesUseMark); - hist_idxstack_indices.add(indicesUseMark); - - // reset marks - indicesUseMark = 0; - } - - // Return arrays: - // indices is kept dirty - if (indices_ref.doCleanRef(indices)) { - indices = indices_ref.putArray(indices); - } - } - - boolean isEmpty() { - return (end == 0); - } - - void reset() { - end = 0; - } - - void push(final int v) { - // remove redundant values (reverse order): - int[] _values = indices; - final int nc = end; - if (nc != 0) { - if (_values[nc - 1] == v) { - // remove both duplicated values: - end--; - return; - } - } - if (_values.length <= nc) { - if (DO_STATS) { - stat_array_idxstack_indices.add(nc + 1); - } - indices = _values = indices_ref.widenArray(_values, nc, nc + 1); - } - _values[end++] = v; - - if (DO_STATS) { - // update used marks: - if (end > indicesUseMark) { - indicesUseMark = end; - } - } - } - - void pullAll(final double[] points, final DPathConsumer2D io, - final boolean moveFirst) - { - final int nc = end; - if (nc == 0) { - return; - } - final int[] _values = indices; - - int i = 0; - - if (moveFirst) { - int j = _values[i] << 1; - io.moveTo(points[j], points[j + 1]); - i++; - } - - for (int j; i < nc; i++) { - j = _values[i] << 1; - io.lineTo(points[j], points[j + 1]); - } - end = 0; - } - } -} diff --git a/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java b/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java index 51dd530..72ffecf 100644 --- a/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java +++ b/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java @@ -65,7 +65,7 @@ public final class DMarlinRenderingEngine extends RenderingEngine private enum NormMode { ON_WITH_AA { @Override - PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, + PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, final PathIterator src) { // NormalizingPathIterator NearestPixelCenter: @@ -74,7 +74,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, }, ON_NO_AA{ @Override - PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, + PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, final PathIterator src) { // NearestPixel NormalizingPathIterator: @@ -83,7 +83,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, }, OFF{ @Override - PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, + PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, final PathIterator src) { // return original path iterator if normalization is disabled: @@ -91,7 +91,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx, } }; - abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx, + abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx, PathIterator src); } @@ -129,7 +129,7 @@ public Shape createStrokedShape(Shape src, float[] dashes, float dashphase) { - final DRendererContext rdrCtx = getRendererContext(); + final RendererContext rdrCtx = getRendererContext(); try { // initialize a large copyable Path2D to avoid a lot of array growing: final Path2D.Double p2d = rdrCtx.getPath2D(); @@ -151,7 +151,7 @@ public Shape createStrokedShape(Shape src, return new Path2D.Double(p2d); } finally { - // recycle the DRendererContext instance + // recycle the RendererContext instance returnRendererContext(rdrCtx); } } @@ -249,7 +249,7 @@ public void strokeTo(Shape src, ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) : NormMode.OFF; - final DRendererContext rdrCtx = getRendererContext(); + final RendererContext rdrCtx = getRendererContext(); try { if ((clip != null) && (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) { @@ -284,12 +284,12 @@ public void strokeTo(Shape src, strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias, rdrCtx.p2dAdapter.init(consumer)); } finally { - // recycle the DRendererContext instance + // recycle the RendererContext instance returnRendererContext(rdrCtx); } } - void strokeTo(final DRendererContext rdrCtx, + void strokeTo(final RendererContext rdrCtx, Shape src, AffineTransform at, BasicStroke bs, @@ -390,7 +390,7 @@ private double userSpaceLineWidth(AffineTransform at, double lw) { return (lw / widthScale); } - void strokeTo(final DRendererContext rdrCtx, + void strokeTo(final RendererContext rdrCtx, Shape src, AffineTransform at, double width, @@ -494,7 +494,7 @@ void strokeTo(final DRendererContext rdrCtx, at = null; } - final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D; + final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D; if (DO_TRACE_PATH) { // trace Stroker: @@ -724,7 +724,7 @@ static final class NearestPixelQuarter } } - private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi, + private static void pathTo(final RendererContext rdrCtx, final PathIterator pi, DPathConsumer2D pc2d) { if (USE_PATH_SIMPLIFIER) { @@ -910,17 +910,17 @@ public AATileGenerator getAATileGenerator(Shape s, int[] bbox) { MarlinTileGenerator ptg = null; - DRenderer r = null; + Renderer r = null; - final DRendererContext rdrCtx = getRendererContext(); + final RendererContext rdrCtx = getRendererContext(); try { if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) { // Define the initial clip bounds: final double[] clipRect = rdrCtx.clipRect; // Adjust the clipping rectangle with the renderer offsets - final double rdrOffX = DRenderer.RDR_OFFSET_X; - final double rdrOffY = DRenderer.RDR_OFFSET_Y; + final double rdrOffX = Renderer.RDR_OFFSET_X; + final double rdrOffY = Renderer.RDR_OFFSET_Y; // add a small rounding error: final double margin = 1e-3d; @@ -1033,9 +1033,9 @@ public AATileGenerator getAATileGenerator(double x, double y, } MarlinTileGenerator ptg = null; - DRenderer r = null; + Renderer r = null; - final DRendererContext rdrCtx = getRendererContext(); + final RendererContext rdrCtx = getRendererContext(); try { r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), @@ -1104,15 +1104,15 @@ public float getMinimumAAPenSize() { } } - // --- DRendererContext handling --- - // use ThreadLocal or ConcurrentLinkedQueue to get one DRendererContext + // --- RendererContext handling --- + // use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext private static final boolean USE_THREAD_LOCAL; // reference type stored in either TL or CLQ static final int REF_TYPE; - // Per-thread DRendererContext - private static final ReentrantContextProvider RDR_CTX_PROVIDER; + // Per-thread RendererContext + private static final ReentrantContextProvider RDR_CTX_PROVIDER; // Static initializer to use TL or CLQ mode static { @@ -1137,19 +1137,19 @@ public float getMinimumAAPenSize() { } if (USE_THREAD_LOCAL) { - RDR_CTX_PROVIDER = new ReentrantContextProviderTL(REF_TYPE) + RDR_CTX_PROVIDER = new ReentrantContextProviderTL(REF_TYPE) { @Override - protected DRendererContext newContext() { - return DRendererContext.createContext(); + protected RendererContext newContext() { + return RendererContext.createContext(); } }; } else { - RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ(REF_TYPE) + RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ(REF_TYPE) { @Override - protected DRendererContext newContext() { - return DRendererContext.createContext(); + protected RendererContext newContext() { + return RendererContext.createContext(); } }; } @@ -1273,26 +1273,26 @@ private static void logSettings(final String reClass) { logInfo("Renderer settings:"); logInfo("SORT = " + MergeSort.SORT_TYPE); - logInfo("CUB_DEC_BND = " + DRenderer.CUB_DEC_BND); - logInfo("CUB_INC_BND = " + DRenderer.CUB_INC_BND); - logInfo("QUAD_DEC_BND = " + DRenderer.QUAD_DEC_BND); + logInfo("CUB_DEC_BND = " + Renderer.CUB_DEC_BND); + logInfo("CUB_INC_BND = " + Renderer.CUB_INC_BND); + logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND); logInfo("INITIAL_EDGES_CAPACITY = " + MarlinConst.INITIAL_EDGES_CAPACITY); logInfo("INITIAL_CROSSING_COUNT = " - + DRenderer.INITIAL_CROSSING_COUNT); + + Renderer.INITIAL_CROSSING_COUNT); logInfo("==========================================================" + "====================="); } /** - * Get the DRendererContext instance dedicated to the current thread - * @return DRendererContext instance + * Get the RendererContext instance dedicated to the current thread + * @return RendererContext instance */ @SuppressWarnings({"unchecked"}) - static DRendererContext getRendererContext() { - final DRendererContext rdrCtx = RDR_CTX_PROVIDER.acquire(); + static RendererContext getRendererContext() { + final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire(); if (DO_MONITORS) { rdrCtx.stats.mon_pre_getAATileGenerator.start(); } @@ -1300,10 +1300,10 @@ static DRendererContext getRendererContext() { } /** - * Reset and return the given DRendererContext instance for reuse - * @param rdrCtx DRendererContext instance + * Reset and return the given RendererContext instance for reuse + * @param rdrCtx RendererContext instance */ - static void returnRendererContext(final DRendererContext rdrCtx) { + static void returnRendererContext(final RendererContext rdrCtx) { rdrCtx.dispose(); if (DO_MONITORS) { diff --git a/src/main/java/sun/java2d/marlin/DPathSimplifier.java b/src/main/java/sun/java2d/marlin/DPathSimplifier.java deleted file mode 100644 index a33ae6c..0000000 --- a/src/main/java/sun/java2d/marlin/DPathSimplifier.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package sun.java2d.marlin; - -final class DPathSimplifier implements DPathConsumer2D { - - // distance threshold in pixels (device) - private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance(); - // squared tolerance in pixels - private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD; - - // members: - private DPathConsumer2D delegate; - // current reference point - private double cx, cy; - // flag indicating if the given point was skipped - private boolean skipped; - // last skipped point - private double sx, sy; - - DPathSimplifier() { - } - - DPathSimplifier init(final DPathConsumer2D delegate) { - if (this.delegate != delegate) { - this.delegate = delegate; - } - skipped = false; - return this; // fluent API - } - - private void finishPath() { - if (skipped) { - _lineTo(sx, sy); - } - } - - @Override - public void pathDone() { - finishPath(); - delegate.pathDone(); - } - - @Override - public void closePath() { - finishPath(); - delegate.closePath(); - } - - @Override - public void moveTo(final double xe, final double ye) { - finishPath(); - delegate.moveTo(xe, ye); - cx = xe; - cy = ye; - } - - @Override - public void lineTo(final double xe, final double ye) { - // Test if segment is too small: - double dx = (xe - cx); - double dy = (ye - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - skipped = true; - sx = xe; - sy = ye; - return; - } - _lineTo(xe, ye); - } - - private void _lineTo(final double xe, final double ye) { - delegate.lineTo(xe, ye); - cx = xe; - cy = ye; - skipped = false; - } - - @Override - public void quadTo(final double x1, final double y1, - final double xe, final double ye) - { - // Test if curve is too small: - double dx = (xe - cx); - double dy = (ye - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - // check control points P1: - dx = (x1 - cx); - dy = (y1 - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - skipped = true; - sx = xe; - sy = ye; - return; - } - } - delegate.quadTo(x1, y1, xe, ye); - cx = xe; - cy = ye; - skipped = false; - } - - @Override - public void curveTo(final double x1, final double y1, - final double x2, final double y2, - final double xe, final double ye) - { - // Test if curve is too small: - double dx = (xe - cx); - double dy = (ye - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - // check control points P1: - dx = (x1 - cx); - dy = (y1 - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - // check control points P2: - dx = (x2 - cx); - dy = (y2 - cy); - - if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { - skipped = true; - sx = xe; - sy = ye; - return; - } - } - } - delegate.curveTo(x1, y1, x2, y2, xe, ye); - cx = xe; - cy = ye; - skipped = false; - } - - @Override - public long getNativeConsumer() { - return 0; - } -} diff --git a/src/main/java/sun/java2d/marlin/DRenderer.java b/src/main/java/sun/java2d/marlin/DRenderer.java deleted file mode 100644 index 3e48172..0000000 --- a/src/main/java/sun/java2d/marlin/DRenderer.java +++ /dev/null @@ -1,1578 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import static sun.java2d.marlin.OffHeapArray.SIZE_INT; -import sun.misc.Unsafe; - -final class DRenderer implements DPathConsumer2D, MarlinRenderer { - - static final boolean DISABLE_RENDER = false; - - static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags(); - static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics(); - - private static final int ALL_BUT_LSB = 0xFFFFFFFE; - private static final int ERR_STEP_MAX = 0x7FFFFFFF; // = 2^31 - 1 - - private static final double POWER_2_TO_32 = 0x1.0p32d; - - // use double to make tosubpix methods faster (no int to double conversion) - static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X; - static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y; - static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1; - static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1; - - static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X; - static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y; - - // number of subpixels corresponding to a tile line - private static final int SUBPIXEL_TILE - = TILE_H << SUBPIXEL_LG_POSITIONS_Y; - - // 2176 pixels (height) x 8 subpixels = 68K - static final int INITIAL_BUCKET_ARRAY - = INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y; - - // crossing capacity = edges count / 4 ~ 1024 - static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2; - - // common to all types of input path segments. - // OFFSET as bytes - // only integer values: - public static final long OFF_CURX_OR = 0; - public static final long OFF_ERROR = OFF_CURX_OR + SIZE_INT; - public static final long OFF_BUMP_X = OFF_ERROR + SIZE_INT; - public static final long OFF_BUMP_ERR = OFF_BUMP_X + SIZE_INT; - public static final long OFF_NEXT = OFF_BUMP_ERR + SIZE_INT; - public static final long OFF_YMAX = OFF_NEXT + SIZE_INT; - - // size of one edge in bytes - public static final int SIZEOF_EDGE_BYTES = (int)(OFF_YMAX + SIZE_INT); - - // curve break into lines - // cubic error in subpixels to decrement step - private static final double CUB_DEC_ERR_SUBPIX - = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel - // cubic error in subpixels to increment step - private static final double CUB_INC_ERR_SUBPIX - = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel - // scale factor for Y-axis contribution to quad / cubic errors: - public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y; - - // TestNonAARasterization (JDK-8170879): cubics - // bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07) -// 2018 - // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max = 9), 4042 warnings (avg = 0,06) - - // cubic bind length to decrement step - public static final double CUB_DEC_BND - = 8.0d * CUB_DEC_ERR_SUBPIX; - // cubic bind length to increment step - public static final double CUB_INC_BND - = 8.0d * CUB_INC_ERR_SUBPIX; - - // cubic countlg - public static final int CUB_COUNT_LG = 2; - // cubic count = 2^countlg - private static final int CUB_COUNT = 1 << CUB_COUNT_LG; - // cubic count^2 = 4^countlg - private static final int CUB_COUNT_2 = 1 << (2 * CUB_COUNT_LG); - // cubic count^3 = 8^countlg - private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG); - // cubic dt = 1 / count - private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT; - // cubic dt^2 = 1 / count^2 = 1 / 4^countlg - private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2; - // cubic dt^3 = 1 / count^3 = 1 / 8^countlg - private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3; - - // quad break into lines - // quadratic error in subpixels - private static final double QUAD_DEC_ERR_SUBPIX - = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel - - // TestNonAARasterization (JDK-8170879): quads - // bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10) -// 2018 - // 0.50px = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10) - - // quadratic bind length to decrement step - public static final double QUAD_DEC_BND - = 8.0d * QUAD_DEC_ERR_SUBPIX; - -////////////////////////////////////////////////////////////////////////////// -// SCAN LINE -////////////////////////////////////////////////////////////////////////////// - // crossings ie subpixel edge x coordinates - private int[] crossings; - // auxiliary storage for crossings (merge sort) - private int[] aux_crossings; - - // indices into the segment pointer lists. They indicate the "active" - // sublist in the segment lists (the portion of the list that contains - // all the segments that cross the next scan line). - private int edgeCount; - private int[] edgePtrs; - // auxiliary storage for edge pointers (merge sort) - private int[] aux_edgePtrs; - - // max used for both edgePtrs and crossings (stats only) - private int activeEdgeMaxUsed; - - // crossings ref (dirty) - private final ArrayCacheInt.Reference crossings_ref; - // edgePtrs ref (dirty) - private final ArrayCacheInt.Reference edgePtrs_ref; - // merge sort initial arrays (large enough to satisfy most usages) (1024) - // aux_crossings ref (dirty) - private final ArrayCacheInt.Reference aux_crossings_ref; - // aux_edgePtrs ref (dirty) - private final ArrayCacheInt.Reference aux_edgePtrs_ref; - -////////////////////////////////////////////////////////////////////////////// -// EDGE LIST -////////////////////////////////////////////////////////////////////////////// - private int edgeMinY = Integer.MAX_VALUE; - private int edgeMaxY = Integer.MIN_VALUE; - private double edgeMinX = Double.POSITIVE_INFINITY; - private double edgeMaxX = Double.NEGATIVE_INFINITY; - - // edges [ints] stored in off-heap memory - private final OffHeapArray edges; - - private int[] edgeBuckets; - private int[] edgeBucketCounts; // 2*newedges + (1 if pruning needed) - // used range for edgeBuckets / edgeBucketCounts - private int buckets_minY; - private int buckets_maxY; - - // edgeBuckets ref (clean) - private final ArrayCacheIntClean.Reference edgeBuckets_ref; - // edgeBucketCounts ref (clean) - private final ArrayCacheIntClean.Reference edgeBucketCounts_ref; - - // Flattens using adaptive forward differencing. This only carries out - // one iteration of the AFD loop. All it does is update AFD variables (i.e. - // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). - private void quadBreakIntoLinesAndAdd(double x0, double y0, - final DCurve c, - final double x2, final double y2) - { - int count = 1; // dt = 1 / count - - // maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1) - double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY; - - final double _DEC_BND = QUAD_DEC_BND; - - while (maxDD >= _DEC_BND) { - // divide step by half: - maxDD /= 4.0d; // error divided by 2^2 = 4 - - count <<= 1; - if (DO_STATS) { - rdrCtx.stats.stat_rdr_quadBreak_dec.add(count); - } - } - - final int nL = count; // line count - - if (count > 1) { - final double icount = 1.0d / count; // dt - final double icount2 = icount * icount; // dt^2 - - final double ddx = c.dbx * icount2; - final double ddy = c.dby * icount2; - double dx = c.bx * icount2 + c.cx * icount; - double dy = c.by * icount2 + c.cy * icount; - - // we use x0, y0 to walk the line - for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) { - x1 += dx; - y1 += dy; - - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - } - addLine(x0, y0, x2, y2); - - if (DO_STATS) { - rdrCtx.stats.stat_rdr_quadBreak.add(nL); - } - } - - // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these - // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce - // numerical errors, and our callers already have the exact values. - // Another alternative would be to pass all the control points, and call - // c.set here, but then too many numbers are passed around. - private void curveBreakIntoLinesAndAdd(double x0, double y0, - final DCurve c, - final double x3, final double y3) - { - int count = CUB_COUNT; - final double icount = CUB_INV_COUNT; // dt - final double icount2 = CUB_INV_COUNT_2; // dt^2 - final double icount3 = CUB_INV_COUNT_3; // dt^3 - - // the dx and dy refer to forward differencing variables, not the last - // coefficients of the "points" polynomial - double dddx, dddy, ddx, ddy, dx, dy; - dddx = 2.0d * c.dax * icount3; - dddy = 2.0d * c.day * icount3; - ddx = dddx + c.dbx * icount2; - ddy = dddy + c.dby * icount2; - dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount; - dy = c.ay * icount3 + c.by * icount2 + c.cy * icount; - - int nL = 0; // line count - - final double _DEC_BND = CUB_DEC_BND; - final double _INC_BND = CUB_INC_BND; - final double _SCALE_DY = SCALE_DY; - - // we use x0, y0 to walk the line - for (double x1 = x0, y1 = y0; count > 0; ) { - // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges - - // double step: - // can only do this on even "count" values, because we must divide count by 2 - while ((count % 2 == 0) - && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) { - dx = 2.0d * dx + ddx; - dy = 2.0d * dy + ddy; - ddx = 4.0d * (ddx + dddx); - ddy = 4.0d * (ddy + dddy); - dddx *= 8.0d; - dddy *= 8.0d; - - count >>= 1; - if (DO_STATS) { - rdrCtx.stats.stat_rdr_curveBreak_inc.add(count); - } - } - - // divide step by half: - while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) { - dddx /= 8.0d; - dddy /= 8.0d; - ddx = ddx / 4.0d - dddx; - ddy = ddy / 4.0d - dddy; - dx = (dx - ddx) / 2.0d; - dy = (dy - ddy) / 2.0d; - - count <<= 1; - if (DO_STATS) { - rdrCtx.stats.stat_rdr_curveBreak_dec.add(count); - } - } - if (--count == 0) { - break; - } - - x1 += dx; - y1 += dy; - dx += ddx; - dy += ddy; - ddx += dddx; - ddy += dddy; - - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - addLine(x0, y0, x3, y3); - - if (DO_STATS) { - rdrCtx.stats.stat_rdr_curveBreak.add(nL + 1); - } - } - - private void addLine(double x1, double y1, double x2, double y2) { - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_addLine.start(); - } - if (DO_STATS) { - rdrCtx.stats.stat_rdr_addLine.add(1); - } - int or = 1; // orientation of the line. 1 if y increases, 0 otherwise. - if (y2 < y1) { - or = 0; - double tmp = y2; - y2 = y1; - y1 = tmp; - tmp = x2; - x2 = x1; - x1 = tmp; - } - - // convert subpixel coordinates [double] into pixel positions [int] - - // The index of the pixel that holds the next HPC is at ceil(trueY - 0.5) - // Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply - // ceil(y1) or ceil(y2) - // upper integer (inclusive) - final int firstCrossing = FloatMath.max(FloatMath.ceil_int(y1), boundsMinY); - - // note: use boundsMaxY (last Y exclusive) to compute correct coverage - // upper integer (exclusive) - final int lastCrossing = FloatMath.min(FloatMath.ceil_int(y2), boundsMaxY); - - /* skip horizontal lines in pixel space and clip edges - out of y range [boundsMinY; boundsMaxY] */ - if (firstCrossing >= lastCrossing) { - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_addLine.stop(); - } - if (DO_STATS) { - rdrCtx.stats.stat_rdr_addLine_skip.add(1); - } - return; - } - - // edge min/max X/Y are in subpixel space (half-open interval): - // note: Use integer crossings to ensure consistent range within - // edgeBuckets / edgeBucketCounts arrays in case of NaN values (int = 0) - if (firstCrossing < edgeMinY) { - edgeMinY = firstCrossing; - } - if (lastCrossing > edgeMaxY) { - edgeMaxY = lastCrossing; - } - - final double slope = (x1 - x2) / (y1 - y2); - - if (slope >= 0.0d) { // <==> x1 < x2 - if (x1 < edgeMinX) { - edgeMinX = x1; - } - if (x2 > edgeMaxX) { - edgeMaxX = x2; - } - } else { - if (x2 < edgeMinX) { - edgeMinX = x2; - } - if (x1 > edgeMaxX) { - edgeMaxX = x1; - } - } - - // local variables for performance: - final int _SIZEOF_EDGE_BYTES = SIZEOF_EDGE_BYTES; - - final OffHeapArray _edges = edges; - - // get free pointer (ie length in bytes) - final int edgePtr = _edges.used; - - // use substraction to avoid integer overflow: - if (_edges.length - edgePtr < _SIZEOF_EDGE_BYTES) { - // suppose _edges.length > _SIZEOF_EDGE_BYTES - // so doubling size is enough to add needed bytes - // note: throw IOOB if neededSize > 2Gb: - final long edgeNewSize = ArrayCacheConst.getNewLargeSize( - _edges.length, - edgePtr + _SIZEOF_EDGE_BYTES); - - if (DO_STATS) { - rdrCtx.stats.stat_rdr_edges_resizes.add(edgeNewSize); - } - _edges.resize(edgeNewSize); - } - - - final Unsafe _unsafe = OffHeapArray.UNSAFE; - final long SIZE_INT = 4L; - long addr = _edges.address + edgePtr; - - // The x value must be bumped up to its position at the next HPC we will evaluate. - // "firstcrossing" is the (sub)pixel number where the next crossing occurs - // thus, the actual coordinate of the next HPC is "firstcrossing + 0.5" - // so the Y distance we cover is "firstcrossing + 0.5 - trueY". - // Note that since y1 (and y2) are already biased by -0.5 in tosubpixy(), we have - // y1 = trueY - 0.5 - // trueY = y1 + 0.5 - // firstcrossing + 0.5 - trueY = firstcrossing + 0.5 - (y1 + 0.5) - // = firstcrossing - y1 - // The x coordinate at that HPC is then: - // x1_intercept = x1 + (firstcrossing - y1) * slope - // The next VPC is then given by: - // VPC index = ceil(x1_intercept - 0.5), or alternately - // VPC index = floor(x1_intercept - 0.5 + 1 - epsilon) - // epsilon is hard to pin down in floating point, but easy in fixed point, so if - // we convert to fixed point then these operations get easier: - // long x1_fixed = x1_intercept * 2^32; (fixed point 32.32 format) - // curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1) - // = fixed_floor(x1_fixed + 2^31 - 1) - // = fixed_floor(x1_fixed + 0x7FFFFFFF) - // and error = fixed_fract(x1_fixed + 0x7FFFFFFF) - final double x1_intercept = x1 + (firstCrossing - y1) * slope; - - // inlined scalb(x1_intercept, 32): - final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept)) - + 0x7FFFFFFFL; - // curx: - // last bit corresponds to the orientation - _unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or); - addr += SIZE_INT; - _unsafe.putInt(addr, ((int) x1_fixed_biased) >>> 1); - addr += SIZE_INT; - - // inlined scalb(slope, 32): - final long slope_fixed = (long) (POWER_2_TO_32 * slope); - - // last bit set to 0 to keep orientation: - _unsafe.putInt(addr, (((int) (slope_fixed >> 31L)) & ALL_BUT_LSB)); - addr += SIZE_INT; - _unsafe.putInt(addr, ((int) slope_fixed) >>> 1); - addr += SIZE_INT; - - final int[] _edgeBuckets = edgeBuckets; - final int[] _edgeBucketCounts = edgeBucketCounts; - - final int _boundsMinY = boundsMinY; - - // each bucket is a linked list. this method adds ptr to the - // start of the "bucket"th linked list. - final int bucketIdx = firstCrossing - _boundsMinY; - - // pointer from bucket - _unsafe.putInt(addr, _edgeBuckets[bucketIdx]); - addr += SIZE_INT; - // y max (exclusive) - _unsafe.putInt(addr, lastCrossing); - - // Update buckets: - // directly the edge struct "pointer" - _edgeBuckets[bucketIdx] = edgePtr; - _edgeBucketCounts[bucketIdx] += 2; // 1 << 1 - // last bit means edge end - _edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1; - - // update free pointer (ie length in bytes) - _edges.used += _SIZEOF_EDGE_BYTES; - - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_addLine.stop(); - } - } - -// END EDGE LIST -////////////////////////////////////////////////////////////////////////////// - - // Cache to store RLE-encoded coverage mask of the current primitive - final MarlinCache cache; - - // Bounds of the drawing region, at subpixel precision. - private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; - - // Current winding rule - private int windingRule; - - // Current drawing position, i.e., final point of last segment - private double x0, y0; - - // Position of most recent 'moveTo' command - private double sx0, sy0; - - // per-thread renderer context - final DRendererContext rdrCtx; - // dirty curve - private final DCurve curve; - - // clean alpha array (zero filled) - private int[] alphaLine; - - // alphaLine ref (clean) - private final ArrayCacheIntClean.Reference alphaLine_ref; - - private boolean enableBlkFlags = false; - private boolean prevUseBlkFlags = false; - - /* block flags (0|1) */ - private int[] blkFlags; - - // blkFlags ref (clean) - private final ArrayCacheIntClean.Reference blkFlags_ref; - - DRenderer(final DRendererContext rdrCtx) { - this.rdrCtx = rdrCtx; - this.curve = rdrCtx.curve; - this.cache = rdrCtx.cache; - - this.edges = rdrCtx.newOffHeapArray(INITIAL_EDGES_CAPACITY); // 96K - - edgeBuckets_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K - edgeBucketCounts_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K - - edgeBuckets = edgeBuckets_ref.initial; - edgeBucketCounts = edgeBucketCounts_ref.initial; - - // 4096 pixels large - alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 16K - alphaLine = alphaLine_ref.initial; - - crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K - aux_crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K - edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K - aux_edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K - - crossings = crossings_ref.initial; - aux_crossings = aux_crossings_ref.initial; - edgePtrs = edgePtrs_ref.initial; - aux_edgePtrs = aux_edgePtrs_ref.initial; - - blkFlags_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line - blkFlags = blkFlags_ref.initial; - } - - DRenderer init(final int pix_boundsX, final int pix_boundsY, - final int pix_boundsWidth, final int pix_boundsHeight, - final int windingRule) - { - this.windingRule = windingRule; - - // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY - this.boundsMinX = pix_boundsX << SUBPIXEL_LG_POSITIONS_X; - this.boundsMaxX = - (pix_boundsX + pix_boundsWidth) << SUBPIXEL_LG_POSITIONS_X; - this.boundsMinY = pix_boundsY << SUBPIXEL_LG_POSITIONS_Y; - this.boundsMaxY = - (pix_boundsY + pix_boundsHeight) << SUBPIXEL_LG_POSITIONS_Y; - - if (DO_LOG_BOUNDS) { - MarlinUtils.logInfo("boundsXY = [" + boundsMinX + " ... " - + boundsMaxX + "[ [" + boundsMinY + " ... " - + boundsMaxY + "["); - } - - // see addLine: ceil(boundsMaxY) => boundsMaxY + 1 - // +1 for edgeBucketCounts - final int edgeBucketsLength = (boundsMaxY - boundsMinY) + 1; - - if (edgeBucketsLength > INITIAL_BUCKET_ARRAY) { - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_edgeBuckets - .add(edgeBucketsLength); - rdrCtx.stats.stat_array_renderer_edgeBucketCounts - .add(edgeBucketsLength); - } - edgeBuckets = edgeBuckets_ref.getArray(edgeBucketsLength); - edgeBucketCounts = edgeBucketCounts_ref.getArray(edgeBucketsLength); - } - - edgeMinY = Integer.MAX_VALUE; - edgeMaxY = Integer.MIN_VALUE; - edgeMinX = Double.POSITIVE_INFINITY; - edgeMaxX = Double.NEGATIVE_INFINITY; - - // reset used mark: - edgeCount = 0; - activeEdgeMaxUsed = 0; - edges.used = 0; - - return this; // fluent API - } - - /** - * Disposes this renderer and recycle it clean up before reusing this instance - */ - void dispose() { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_activeEdges.add(activeEdgeMaxUsed); - rdrCtx.stats.stat_rdr_edges.add(edges.used); - rdrCtx.stats.stat_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES); - rdrCtx.stats.hist_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES); - rdrCtx.stats.totalOffHeap += edges.length; - } - // Return arrays: - if (crossings_ref.doCleanRef(crossings)) { - crossings = crossings_ref.putArray(crossings); - } - if (aux_crossings_ref.doCleanRef(aux_crossings)) { - aux_crossings = aux_crossings_ref.putArray(aux_crossings); - } - - if (edgePtrs_ref.doCleanRef(edgePtrs)) { - edgePtrs = edgePtrs_ref.putArray(edgePtrs); - } - if (aux_edgePtrs_ref.doCleanRef(aux_edgePtrs)) { - aux_edgePtrs = aux_edgePtrs_ref.putArray(aux_edgePtrs); - } - - if (alphaLine_ref.doSetRef(alphaLine)) { - alphaLine = alphaLine_ref.putArrayClean(alphaLine); // already zero filled - } - if (blkFlags_ref.doSetRef(blkFlags)) { - blkFlags = blkFlags_ref.putArrayClean(blkFlags); // already zero filled - } - - if (edgeMinY != Integer.MAX_VALUE) { - // if context is maked as DIRTY: - if (rdrCtx.dirty) { - // may happen if an exception if thrown in the pipeline processing: - // clear completely buckets arrays: - buckets_minY = 0; - buckets_maxY = boundsMaxY - boundsMinY; - } - // clear only used part - edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, buckets_minY, - buckets_maxY); - edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts, - buckets_minY, - buckets_maxY + 1); - } else { - // unused arrays - if (edgeBuckets_ref.doSetRef(edgeBuckets)) { - edgeBuckets = edgeBuckets_ref.putArrayClean(edgeBuckets); - } - if (edgeBucketCounts_ref.doSetRef(edgeBucketCounts)) { - edgeBucketCounts = edgeBucketCounts_ref.putArrayClean(edgeBucketCounts); - } - } - - // At last: resize back off-heap edges to initial size - if (edges.length != INITIAL_EDGES_CAPACITY) { - // note: may throw OOME: - edges.resize(INITIAL_EDGES_CAPACITY); - } - if (DO_CLEAN_DIRTY) { - // Force zero-fill dirty arrays: - edges.fill(BYTE_0); - } - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_endRendering.stop(); - } - // recycle the RendererContext instance - DMarlinRenderingEngine.returnRendererContext(rdrCtx); - } - - private static double tosubpixx(final double pix_x) { - return SUBPIXEL_SCALE_X * pix_x; - } - - private static double tosubpixy(final double pix_y) { - // shift y by -0.5 for fast ceil(y - 0.5): - return SUBPIXEL_SCALE_Y * pix_y - 0.5d; - } - - @Override - public void moveTo(final double pix_x0, final double pix_y0) { - closePath(); - final double sx = tosubpixx(pix_x0); - final double sy = tosubpixy(pix_y0); - this.sx0 = sx; - this.sy0 = sy; - this.x0 = sx; - this.y0 = sy; - } - - @Override - public void lineTo(final double pix_x1, final double pix_y1) { - final double x1 = tosubpixx(pix_x1); - final double y1 = tosubpixy(pix_y1); - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - - @Override - public void curveTo(final double pix_x1, final double pix_y1, - final double pix_x2, final double pix_y2, - final double pix_x3, final double pix_y3) - { - final double xe = tosubpixx(pix_x3); - final double ye = tosubpixy(pix_y3); - curve.set(x0, y0, - tosubpixx(pix_x1), tosubpixy(pix_y1), - tosubpixx(pix_x2), tosubpixy(pix_y2), - xe, ye); - curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye); - x0 = xe; - y0 = ye; - } - - @Override - public void quadTo(final double pix_x1, final double pix_y1, - final double pix_x2, final double pix_y2) - { - final double xe = tosubpixx(pix_x2); - final double ye = tosubpixy(pix_y2); - curve.set(x0, y0, - tosubpixx(pix_x1), tosubpixy(pix_y1), - xe, ye); - quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye); - x0 = xe; - y0 = ye; - } - - @Override - public void closePath() { - if (x0 != sx0 || y0 != sy0) { - addLine(x0, y0, sx0, sy0); - x0 = sx0; - y0 = sy0; - } - } - - @Override - public void pathDone() { - closePath(); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Renderer does not use a native consumer."); - } - - private void _endRendering(final int ymin, final int ymax) { - if (DISABLE_RENDER) { - return; - } - - // Get X bounds as true pixel boundaries to compute correct pixel coverage: - final int bboxx0 = bbox_spminX; - final int bboxx1 = bbox_spmaxX; - - final boolean windingRuleEvenOdd = (windingRule == WIND_EVEN_ODD); - - // Useful when processing tile line by tile line - final int[] _alpha = alphaLine; - - // local vars (performance): - final MarlinCache _cache = cache; - final OffHeapArray _edges = edges; - final int[] _edgeBuckets = edgeBuckets; - final int[] _edgeBucketCounts = edgeBucketCounts; - - int[] _crossings = this.crossings; - int[] _edgePtrs = this.edgePtrs; - - // merge sort auxiliary storage: - int[] _aux_crossings = this.aux_crossings; - int[] _aux_edgePtrs = this.aux_edgePtrs; - - // copy constants: - final long _OFF_ERROR = OFF_ERROR; - final long _OFF_BUMP_X = OFF_BUMP_X; - final long _OFF_BUMP_ERR = OFF_BUMP_ERR; - - final long _OFF_NEXT = OFF_NEXT; - final long _OFF_YMAX = OFF_YMAX; - - final int _ALL_BUT_LSB = ALL_BUT_LSB; - final int _ERR_STEP_MAX = ERR_STEP_MAX; - - // unsafe I/O: - final Unsafe _unsafe = OffHeapArray.UNSAFE; - final long addr0 = _edges.address; - long addr; - final int _SUBPIXEL_LG_POSITIONS_X = SUBPIXEL_LG_POSITIONS_X; - final int _SUBPIXEL_LG_POSITIONS_Y = SUBPIXEL_LG_POSITIONS_Y; - final int _SUBPIXEL_MASK_X = SUBPIXEL_MASK_X; - final int _SUBPIXEL_MASK_Y = SUBPIXEL_MASK_Y; - final int _SUBPIXEL_POSITIONS_X = SUBPIXEL_POSITIONS_X; - - final int _MIN_VALUE = Integer.MIN_VALUE; - final int _MAX_VALUE = Integer.MAX_VALUE; - - // Now we iterate through the scanlines. We must tell emitRow the coord - // of the first non-transparent pixel, so we must keep accumulators for - // the first and last pixels of the section of the current pixel row - // that we will emit. - // We also need to accumulate pix_bbox, but the iterator does it - // for us. We will just get the values from it once this loop is done - int minX = _MAX_VALUE; - int maxX = _MIN_VALUE; - - int y = ymin; - int bucket = y - boundsMinY; - - int numCrossings = this.edgeCount; - int edgePtrsLen = _edgePtrs.length; - int crossingsLen = _crossings.length; - int _arrayMaxUsed = activeEdgeMaxUsed; - int ptrLen = 0, newCount, ptrEnd; - - int bucketcount, i, j, ecur; - int cross, lastCross; - int x0, x1, tmp, sum, prev, curx, curxo, crorientation, err; - int pix_x, pix_xmaxm1, pix_xmax; - - int low, high, mid, prevNumCrossings; - boolean useBinarySearch; - - final int[] _blkFlags = blkFlags; - final int _BLK_SIZE_LG = BLOCK_SIZE_LG; - final int _BLK_SIZE = BLOCK_SIZE; - - final boolean _enableBlkFlagsHeuristics = ENABLE_BLOCK_FLAGS_HEURISTICS && this.enableBlkFlags; - - // Use block flags if large pixel span and few crossings: - // ie mean(distance between crossings) is high - boolean useBlkFlags = this.prevUseBlkFlags; - - final int stroking = rdrCtx.stroking; - - int lastY = -1; // last emited row - - final DPQSSorterContext sorter = rdrCtx.sorterCtx; - boolean skipISort, useDPQS; - - // Iteration on scanlines - for (; y < ymax; y++, bucket++) { - // --- from former ScanLineIterator.next() - bucketcount = _edgeBucketCounts[bucket]; - - // marker on previously sorted edges: - prevNumCrossings = numCrossings; - - // bucketCount indicates new edge / edge end: - if (bucketcount != 0) { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_activeEdges_updates.add(prevNumCrossings); - } - - // last bit set to 1 means that edges ends - if ((bucketcount & 0x1) != 0) { - // eviction in active edge list - // cache edges[] address + offset - addr = addr0 + _OFF_YMAX; - - for (i = 0, newCount = 0; i < prevNumCrossings; i++) { - // get the pointer to the edge - ecur = _edgePtrs[i]; - // random access so use unsafe: - if (_unsafe.getInt(addr + ecur) > y) { - _edgePtrs[newCount++] = ecur; - } - } - // update marker on sorted edges minus removed edges: - prevNumCrossings = numCrossings = newCount; - } - - ptrLen = bucketcount >> 1; // number of new edge - - if (ptrLen != 0) { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_activeEdges_adds.add(ptrLen); - if (ptrLen > 10) { - rdrCtx.stats.stat_rdr_activeEdges_adds_high.add(ptrLen); - } - } - ptrEnd = numCrossings + ptrLen; - - if (edgePtrsLen < ptrEnd) { - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd); - } - this.edgePtrs = _edgePtrs - = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // bad mark ? TODO: fix edge ptr mark - ptrEnd); - - edgePtrsLen = _edgePtrs.length; - // Get larger auxiliary storage: - aux_edgePtrs_ref.putArray(_aux_edgePtrs); - - // use ArrayCache.getNewSize() to use the same growing - // factor than widenArray(): - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_aux_edgePtrs.add(ptrEnd); - } - this.aux_edgePtrs = _aux_edgePtrs - = aux_edgePtrs_ref.getArray( - ArrayCacheConst.getNewSize(numCrossings, ptrEnd) - ); - } - - // cache edges[] address + offset - addr = addr0 + _OFF_NEXT; - - // add new edges to active edge list: - for (ecur = _edgeBuckets[bucket]; - numCrossings < ptrEnd; numCrossings++) - { - // store the pointer to the edge - _edgePtrs[numCrossings] = ecur; - // random access so use unsafe: - ecur = _unsafe.getInt(addr + ecur); - } - - if (crossingsLen < numCrossings) { - // Get larger array: - crossings_ref.putArray(_crossings); - - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_crossings - .add(numCrossings); - } - this.crossings = _crossings - = crossings_ref.getArray(numCrossings); - - // Get larger auxiliary storage: - aux_crossings_ref.putArray(_aux_crossings); - - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_aux_crossings - .add(numCrossings); - } - this.aux_crossings = _aux_crossings - = aux_crossings_ref.getArray(numCrossings); - - crossingsLen = _crossings.length; - } - if (DO_STATS) { - // update max used mark - if (numCrossings > _arrayMaxUsed) { - _arrayMaxUsed = numCrossings; - } - } - } // ptrLen != 0 - } // bucketCount != 0 - - - if (numCrossings != 0) { - /* - * thresholds to switch to optimized merge sort - * for newly added edges + final merge pass. - */ - if (((numCrossings <= 40) || ((ptrLen <= 10) && (numCrossings <= MergeSort.DISABLE_ISORT_THRESHOLD)))) { - if (DO_STATS) { - rdrCtx.stats.hist_rdr_crossings.add(numCrossings); - rdrCtx.stats.hist_rdr_crossings_adds.add(ptrLen); - } - - /* - * threshold to use binary insertion sort instead of - * straight insertion sort (to reduce minimize comparisons). - */ - useBinarySearch = (numCrossings >= 20); - - // if small enough: - lastCross = _MIN_VALUE; - - for (i = 0; i < numCrossings; i++) { - // get the pointer to the edge - ecur = _edgePtrs[i]; - - /* convert subpixel coordinates into pixel - positions for coming scanline */ - /* note: it is faster to always update edges even - if it is removed from AEL for coming or last scanline */ - - // random access so use unsafe: - addr = addr0 + ecur; // ecur + OFF_F_CURX - - // get current crossing: - curx = _unsafe.getInt(addr); - - // update crossing with orientation at last bit: - cross = curx; - - // Increment x using DDA (fixed point): - curx += _unsafe.getInt(addr + _OFF_BUMP_X); - - // Increment error: - err = _unsafe.getInt(addr + _OFF_ERROR) - + _unsafe.getInt(addr + _OFF_BUMP_ERR); - - // Manual carry handling: - // keep sign and carry bit only and ignore last bit (preserve orientation): - _unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB)); - _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX)); - - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings); - } - - // insertion sort of crossings: - if (cross < lastCross) { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_sorts.add(i); - } - - /* use binary search for newly added edges - in crossings if arrays are large enough */ - if (useBinarySearch && (i >= prevNumCrossings)) { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_bsearch.add(i); - } - low = 0; - high = i - 1; - - do { - // note: use signed shift (not >>>) for performance - // as indices are small enough to exceed Integer.MAX_VALUE - mid = (low + high) >> 1; - - if (_crossings[mid] < cross) { - low = mid + 1; - } else { - high = mid - 1; - } - } while (low <= high); - - for (j = i - 1; j >= low; j--) { - _crossings[j + 1] = _crossings[j]; - _edgePtrs [j + 1] = _edgePtrs[j]; - } - _crossings[low] = cross; - _edgePtrs [low] = ecur; - - } else { - j = i - 1; - _crossings[i] = _crossings[j]; - _edgePtrs[i] = _edgePtrs[j]; - - while ((--j >= 0) && (_crossings[j] > cross)) { - _crossings[j + 1] = _crossings[j]; - _edgePtrs [j + 1] = _edgePtrs[j]; - } - _crossings[j + 1] = cross; - _edgePtrs [j + 1] = ecur; - } - - } else { - _crossings[i] = lastCross = cross; - } - } - } else { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_msorts.add(numCrossings); - rdrCtx.stats.hist_rdr_crossings_ratio - .add((1000 * ptrLen) / numCrossings); - rdrCtx.stats.hist_rdr_crossings_msorts.add(numCrossings); - rdrCtx.stats.hist_rdr_crossings_msorts_adds.add(ptrLen); - } - - // Copy sorted data in auxiliary arrays - // and perform insertion sort on almost sorted data - // (ie i < prevNumCrossings): - - skipISort = (prevNumCrossings >= MergeSort.DISABLE_ISORT_THRESHOLD); - useDPQS = MergeSort.USE_DPQS && (skipISort || (ptrLen >= MergeSort.DPQS_THRESHOLD)); - - if (DO_STATS && useDPQS) { - rdrCtx.stats.stat_rdr_crossings_dpqs.add((skipISort) ? numCrossings : ptrLen); - } - - lastCross = _MIN_VALUE; - - for (i = 0; i < numCrossings; i++) { - // get the pointer to the edge - ecur = _edgePtrs[i]; - - /* convert subpixel coordinates into pixel - positions for coming scanline */ - /* note: it is faster to always update edges even - if it is removed from AEL for coming or last scanline */ - - // random access so use unsafe: - addr = addr0 + ecur; // ecur + OFF_F_CURX - - // get current crossing: - curx = _unsafe.getInt(addr); - - // update crossing with orientation at last bit: - cross = curx; - - // Increment x using DDA (fixed point): - curx += _unsafe.getInt(addr + _OFF_BUMP_X); - - // Increment error: - err = _unsafe.getInt(addr + _OFF_ERROR) - + _unsafe.getInt(addr + _OFF_BUMP_ERR); - - // Manual carry handling: - // keep sign and carry bit only and ignore last bit (preserve orientation): - _unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB)); - _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX)); - - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings); - } - - if (skipISort) { - if (useDPQS) { - // simply store crossing as edgePtrs is in-place: - // will be sorted efficiently by DPQS later: - _crossings[i] = cross; - } else { - // store crossing/edgePtrs in auxiliary arrays: - // will be sorted efficiently by MergeSort later: - _aux_crossings[i] = cross; - _aux_edgePtrs [i] = ecur; - } - } else if (i >= prevNumCrossings) { - if (useDPQS) { - // store crossing/edgePtrs in auxiliary arrays: - // will be sorted efficiently by DPQS later: - _aux_crossings[i] = cross; - _aux_edgePtrs [i] = ecur; - } else { - // simply store crossing as edgePtrs is in-place: - // will be sorted efficiently by MergeSort later: - _crossings[i] = cross; - } - } else { - if (cross < lastCross) { - if (DO_STATS) { - rdrCtx.stats.stat_rdr_crossings_sorts.add(i); - } - // (straight) insertion sort of crossings: - j = i - 1; - _aux_crossings[i] = _aux_crossings[j]; - _aux_edgePtrs [i] = _aux_edgePtrs[j]; - - while ((--j >= 0) && (_aux_crossings[j] > cross)) { - _aux_crossings[j + 1] = _aux_crossings[j]; - _aux_edgePtrs [j + 1] = _aux_edgePtrs[j]; - } - _aux_crossings[j + 1] = cross; - _aux_edgePtrs [j + 1] = ecur; - } else { - // auxiliary storage: - _aux_crossings[i] = lastCross = cross; - _aux_edgePtrs [i] = ecur; - } - } - } - - // use Mergesort using auxiliary arrays (sort only right part) - MergeSort.mergeSortNoCopy(_crossings, _edgePtrs, - _aux_crossings, _aux_edgePtrs, - numCrossings, prevNumCrossings, - skipISort, sorter, useDPQS - ); - } - - // reset ptrLen - ptrLen = 0; - // --- from former ScanLineIterator.next() - - - /* note: bboxx0 and bboxx1 must be pixel boundaries - to have correct coverage computation */ - - // right shift on crossings to get the x-coordinate: - curxo = _crossings[0]; - x0 = curxo >> 1; - if (x0 < minX) { - minX = x0; // subpixel coordinate - } - - x1 = _crossings[numCrossings - 1] >> 1; - if (x1 > maxX) { - maxX = x1; // subpixel coordinate - } - - - // compute pixel coverages - prev = curx = x0; - // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1. - // last bit contains orientation (0 or 1) - crorientation = ((curxo & 0x1) << 1) - 1; - - if (windingRuleEvenOdd) { - sum = crorientation; - - // Even Odd winding rule: take care of mask ie sum(orientations) - for (i = 1; i < numCrossings; i++) { - curxo = _crossings[i]; - curx = curxo >> 1; - // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1. - // last bit contains orientation (0 or 1) - crorientation = ((curxo & 0x1) << 1) - 1; - - if ((sum & 0x1) != 0) { - // TODO: perform line clipping on left-right sides - // to avoid such bound checks: - x0 = (prev > bboxx0) ? prev : bboxx0; - - if (curx < bboxx1) { - x1 = curx; - } else { - x1 = bboxx1; - // skip right side (fast exit loop): - i = numCrossings; - } - - if (x0 < x1) { - x0 -= bboxx0; // turn x0, x1 from coords to indices - x1 -= bboxx0; // in the alpha array. - - pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X; - pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X; - - if (pix_x == pix_xmaxm1) { - // Start and end in same pixel - tmp = (x1 - x0); // number of subpixels - _alpha[pix_x ] += tmp; - _alpha[pix_x + 1] -= tmp; - - if (useBlkFlags) { - // flag used blocks: - // note: block processing handles extra pixel: - _blkFlags[pix_x >> _BLK_SIZE_LG] = 1; - } - } else { - tmp = (x0 & _SUBPIXEL_MASK_X); - _alpha[pix_x ] - += (_SUBPIXEL_POSITIONS_X - tmp); - _alpha[pix_x + 1] - += tmp; - - pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X; - - tmp = (x1 & _SUBPIXEL_MASK_X); - _alpha[pix_xmax ] - -= (_SUBPIXEL_POSITIONS_X - tmp); - _alpha[pix_xmax + 1] - -= tmp; - - if (useBlkFlags) { - // flag used blocks: - // note: block processing handles extra pixel: - _blkFlags[pix_x >> _BLK_SIZE_LG] = 1; - _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1; - } - } - } - } - - sum += crorientation; - prev = curx; - } - } else { - // Non-zero winding rule: optimize that case (default) - // and avoid processing intermediate crossings - for (i = 1, sum = 0;; i++) { - sum += crorientation; - - if (sum != 0) { - // prev = min(curx) - if (prev > curx) { - prev = curx; - } - } else { - // TODO: perform line clipping on left-right sides - // to avoid such bound checks: - x0 = (prev > bboxx0) ? prev : bboxx0; - - if (curx < bboxx1) { - x1 = curx; - } else { - x1 = bboxx1; - // skip right side (fast exit loop): - i = numCrossings; - } - - if (x0 < x1) { - x0 -= bboxx0; // turn x0, x1 from coords to indices - x1 -= bboxx0; // in the alpha array. - - pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X; - pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X; - - if (pix_x == pix_xmaxm1) { - // Start and end in same pixel - tmp = (x1 - x0); // number of subpixels - _alpha[pix_x ] += tmp; - _alpha[pix_x + 1] -= tmp; - - if (useBlkFlags) { - // flag used blocks: - // note: block processing handles extra pixel: - _blkFlags[pix_x >> _BLK_SIZE_LG] = 1; - } - } else { - tmp = (x0 & _SUBPIXEL_MASK_X); - _alpha[pix_x ] - += (_SUBPIXEL_POSITIONS_X - tmp); - _alpha[pix_x + 1] - += tmp; - - pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X; - - tmp = (x1 & _SUBPIXEL_MASK_X); - _alpha[pix_xmax ] - -= (_SUBPIXEL_POSITIONS_X - tmp); - _alpha[pix_xmax + 1] - -= tmp; - - if (useBlkFlags) { - // flag used blocks: - // note: block processing handles extra pixel: - _blkFlags[pix_x >> _BLK_SIZE_LG] = 1; - _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1; - } - } - } - prev = _MAX_VALUE; - } - - if (i == numCrossings) { - break; - } - - curxo = _crossings[i]; - curx = curxo >> 1; - // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1. - // last bit contains orientation (0 or 1) - crorientation = ((curxo & 0x1) << 1) - 1; - } - } - } // numCrossings > 0 - - // even if this last row had no crossings, alpha will be zeroed - // from the last emitRow call. But this doesn't matter because - // maxX < minX, so no row will be emitted to the MarlinCache. - if ((y & _SUBPIXEL_MASK_Y) == _SUBPIXEL_MASK_Y) { - lastY = y >> _SUBPIXEL_LG_POSITIONS_Y; - - // convert subpixel to pixel coordinate within boundaries: - minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X; - maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X; - - if (maxX >= minX) { - // note: alpha array will be zeroed by copyAARow() - // +1 because alpha [pix_minX; pix_maxX[ - // fix range [x0; x1[ - // note: if x1=bboxx1, then alpha is written up to bboxx1+1 - // inclusive: alpha[bboxx1] ignored, alpha[bboxx1+1] == 0 - // (normally so never cleared below) - copyAARow(_alpha, lastY, minX, maxX + 1, useBlkFlags); - - // speculative for next pixel row (scanline coherence): - if (_enableBlkFlagsHeuristics) { - // Use block flags if large pixel span and few crossings: - // ie mean(distance between crossings) is larger than - // 1 block size; - - // fast check width: - maxX -= minX; - - // if stroking: numCrossings /= 2 - // => shift numCrossings by 1 - // condition = (width / (numCrossings - 1)) > blockSize - useBlkFlags = (maxX > _BLK_SIZE) && (maxX > - (((numCrossings >> stroking) - 1) << _BLK_SIZE_LG)); - - if (DO_STATS) { - tmp = FloatMath.max(1, - ((numCrossings >> stroking) - 1)); - rdrCtx.stats.hist_tile_generator_encoding_dist - .add(maxX / tmp); - } - } - } else { - _cache.clearAARow(lastY); - } - minX = _MAX_VALUE; - maxX = _MIN_VALUE; - } - } // scan line iterator - - // Emit final row - y--; - y >>= _SUBPIXEL_LG_POSITIONS_Y; - - // convert subpixel to pixel coordinate within boundaries: - minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X; - maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X; - - if (maxX >= minX) { - // note: alpha array will be zeroed by copyAARow() - // +1 because alpha [pix_minX; pix_maxX[ - // fix range [x0; x1[ - // note: if x1=bboxx1, then alpha is written up to bboxx1+1 - // inclusive: alpha[bboxx1] ignored then cleared and - // alpha[bboxx1+1] == 0 (normally so never cleared after) - copyAARow(_alpha, y, minX, maxX + 1, useBlkFlags); - } else if (y != lastY) { - _cache.clearAARow(y); - } - - // update member: - edgeCount = numCrossings; - prevUseBlkFlags = useBlkFlags; - - if (DO_STATS) { - // update max used mark - activeEdgeMaxUsed = _arrayMaxUsed; - } - } - - boolean endRendering() { - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_endRendering.start(); - } - if (edgeMinY == Integer.MAX_VALUE) { - return false; // undefined edges bounds - } - - // bounds as half-open intervals - final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX); - final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX); - - // edge Min/Max Y are already rounded to subpixels within bounds: - final int spminY = edgeMinY; - final int spmaxY = edgeMaxY; - - buckets_minY = spminY - boundsMinY; - buckets_maxY = spmaxY - boundsMinY; - - if (DO_LOG_BOUNDS) { - MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX - + "[ [" + edgeMinY + " ... " + edgeMaxY + "["); - MarlinUtils.logInfo("spXY = [" + spminX + " ... " + spmaxX - + "[ [" + spminY + " ... " + spmaxY + "["); - } - - // test clipping for shapes out of bounds - if ((spminX >= spmaxX) || (spminY >= spmaxY)) { - return false; - } - - // half open intervals - // inclusive: - final int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X; - // exclusive: - final int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X; - // inclusive: - final int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y; - // exclusive: - final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y; - - // store BBox to answer ptg.getBBox(): - this.cache.init(pminX, pminY, pmaxX, pmaxY); - - // Heuristics for using block flags: - if (ENABLE_BLOCK_FLAGS) { - enableBlkFlags = this.cache.useRLE; - prevUseBlkFlags = enableBlkFlags && !ENABLE_BLOCK_FLAGS_HEURISTICS; - - if (enableBlkFlags) { - // ensure blockFlags array is large enough: - // note: +2 to ensure enough space left at end - final int blkLen = ((pmaxX - pminX) >> BLOCK_SIZE_LG) + 2; - if (blkLen > INITIAL_ARRAY) { - blkFlags = blkFlags_ref.getArray(blkLen); - } - } - } - - // memorize the rendering bounding box: - /* note: bbox_spminX and bbox_spmaxX must be pixel boundaries - to have correct coverage computation */ - // inclusive: - bbox_spminX = pminX << SUBPIXEL_LG_POSITIONS_X; - // exclusive: - bbox_spmaxX = pmaxX << SUBPIXEL_LG_POSITIONS_X; - // inclusive: - bbox_spminY = spminY; - // exclusive: - bbox_spmaxY = spmaxY; - - if (DO_LOG_BOUNDS) { - MarlinUtils.logInfo("pXY = [" + pminX + " ... " + pmaxX - + "[ [" + pminY + " ... " + pmaxY + "["); - MarlinUtils.logInfo("bbox_spXY = [" + bbox_spminX + " ... " - + bbox_spmaxX + "[ [" + bbox_spminY + " ... " - + bbox_spmaxY + "["); - } - - // Prepare alpha line: - // add 2 to better deal with the last pixel in a pixel row. - final int width = (pmaxX - pminX) + 2; - - // Useful when processing tile line by tile line - if (width > INITIAL_AA_ARRAY) { - if (DO_STATS) { - rdrCtx.stats.stat_array_renderer_alphaline.add(width); - } - alphaLine = alphaLine_ref.getArray(width); - } - - // process first tile line: - endRendering(pminY); - - return true; - } - - private int bbox_spminX, bbox_spmaxX, bbox_spminY, bbox_spmaxY; - - void endRendering(final int pminY) { - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_endRendering_Y.start(); - } - - final int spminY = pminY << SUBPIXEL_LG_POSITIONS_Y; - final int fixed_spminY = FloatMath.max(bbox_spminY, spminY); - - // avoid rendering for last call to nextTile() - if (fixed_spminY < bbox_spmaxY) { - // process a complete tile line ie scanlines for 32 rows - final int spmaxY = FloatMath.min(bbox_spmaxY, spminY + SUBPIXEL_TILE); - - // process tile line [0 - 32] - cache.resetTileLine(pminY); - - // Process only one tile line: - _endRendering(fixed_spminY, spmaxY); - } - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_endRendering_Y.stop(); - } - } - - void copyAARow(final int[] alphaRow, - final int pix_y, final int pix_from, final int pix_to, - final boolean useBlockFlags) - { - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_copyAARow.start(); - } - if (useBlockFlags) { - if (DO_STATS) { - rdrCtx.stats.hist_tile_generator_encoding.add(1); - } - cache.copyAARowRLE_WithBlockFlags(blkFlags, alphaRow, pix_y, pix_from, pix_to); - } else { - if (DO_STATS) { - rdrCtx.stats.hist_tile_generator_encoding.add(0); - } - cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to); - } - if (DO_MONITORS) { - rdrCtx.stats.mon_rdr_copyAARow.stop(); - } - } -} diff --git a/src/main/java/sun/java2d/marlin/DRendererContext.java b/src/main/java/sun/java2d/marlin/DRendererContext.java deleted file mode 100644 index 4719f95..0000000 --- a/src/main/java/sun/java2d/marlin/DRendererContext.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.awt.geom.Path2D; -import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicInteger; -import sun.java2d.ReentrantContext; -import sun.java2d.marlin.ArrayCacheConst.CacheStats; -import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter; - -/** - * This class is a renderer context dedicated to a single thread - */ -final class DRendererContext extends ReentrantContext implements IRendererContext { - - // RendererContext creation counter - private static final AtomicInteger CTX_COUNT = new AtomicInteger(1); - - /** - * Create a new renderer context - * - * @return new RendererContext instance - */ - static DRendererContext createContext() { - return new DRendererContext("ctx" - + Integer.toString(CTX_COUNT.getAndIncrement())); - } - - // Smallest object used as Cleaner's parent reference - private final Object cleanerObj; - // dirty flag indicating an exception occured during pipeline in pathTo() - boolean dirty = false; - // shared data - final double[] double6 = new double[6]; - // shared curve (dirty) (Renderer / Stroker) - final DCurve curve = new DCurve(); - // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter: - final NormalizingPathIterator nPCPathIterator; - // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator: - final NormalizingPathIterator nPQPathIterator; - // MarlinRenderingEngine.TransformingPathConsumer2D - final DTransformingPathConsumer2D transformerPC2D; // TODO: clear if dirty ? - // recycled Path2D instance (weak) - private WeakReference refPath2D = null; - final DRenderer renderer; - final DStroker stroker; - // Simplifies out collinear lines - final DCollinearSimplifier simplifier = new DCollinearSimplifier(); - // Simplifies path - final DPathSimplifier pathSimplifier = new DPathSimplifier(); - final DDasher dasher; - final MarlinTileGenerator ptg; - final MarlinCache cache; - // flag indicating the shape is stroked (1) or filled (0) - int stroking = 0; - // flag indicating to clip the shape - boolean doClip = false; - // flag indicating if the path is closed or not (in advance) to handle properly caps - boolean closedPath = false; - // clip rectangle (ymin, ymax, xmin, xmax): - final double[] clipRect = new double[4]; - // clip inverse scale (mean) to adjust length checks - double clipInvScale = 0.0d; - // CurveBasicMonotonizer instance - final CurveBasicMonotonizer monotonizer; - // flag indicating to force the stroker to process joins - boolean isFirstSegment = true; - // CurveClipSplitter instance - final CurveClipSplitter curveClipSplitter; - // DPQS Sorter context - final DPQSSorterContext sorterCtx; - - // Array caches: - /* clean int[] cache (zero-filled) = 5 refs */ - private final ArrayCacheIntClean cleanIntCache = new ArrayCacheIntClean(5); - /* dirty int[] cache = 5 refs */ - private final ArrayCacheInt dirtyIntCache = new ArrayCacheInt(5); - /* dirty double[] cache = 4 refs (2 polystack) */ - private final ArrayCacheDouble dirtyDoubleCache = new ArrayCacheDouble(4); - /* dirty byte[] cache = 2 ref (2 polystack) */ - private final ArrayCacheByte dirtyByteCache = new ArrayCacheByte(2); - - // RendererContext statistics - final RendererStats stats; - - final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter(); - - /** - * Constructor - * - * @param name context name (debugging) - */ - DRendererContext(final String name) { - if (LOG_CREATE_CONTEXT) { - MarlinUtils.logInfo("new RendererContext = " + name); - } - this.cleanerObj = new Object(); - - // create first stats (needed by newOffHeapArray): - if (DO_STATS || DO_MONITORS) { - stats = RendererStats.createInstance(cleanerObj, name); - // push cache stats: - stats.cacheStats = new CacheStats[] { cleanIntCache.stats, - dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats - }; - } else { - stats = null; - } - - // NormalizingPathIterator instances: - nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6); - nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6); - - // curve monotonizer & clip subdivider (before transformerPC2D init) - monotonizer = new CurveBasicMonotonizer(this); - curveClipSplitter = new CurveClipSplitter(this); - - // MarlinRenderingEngine.TransformingPathConsumer2D - transformerPC2D = new DTransformingPathConsumer2D(this); - - // Renderer: - cache = new MarlinCache(this); - renderer = new DRenderer(this); // needs MarlinCache from rdrCtx.cache - ptg = new MarlinTileGenerator(stats, renderer, cache); - - stroker = new DStroker(this); - dasher = new DDasher(this); - - sorterCtx = (MergeSort.USE_DPQS) ? new DPQSSorterContext() : null; - } - - /** - * Disposes this renderer context: - * clean up before reusing this context - */ - void dispose() { - if (DO_STATS) { - if (stats.totalOffHeap > stats.totalOffHeapMax) { - stats.totalOffHeapMax = stats.totalOffHeap; - } - stats.totalOffHeap = 0L; - } - stroking = 0; - doClip = false; - closedPath = false; - clipInvScale = 0.0d; - isFirstSegment = true; - - // if context is maked as DIRTY: - if (dirty) { - // may happen if an exception if thrown in the pipeline processing: - // force cleanup of all possible pipelined blocks (except Renderer): - - // NormalizingPathIterator instances: - this.nPCPathIterator.dispose(); - this.nPQPathIterator.dispose(); - // Dasher: - this.dasher.dispose(); - // Stroker: - this.stroker.dispose(); - - // mark context as CLEAN: - dirty = false; - } - } - - Path2D.Double getPath2D() { - // resolve reference: - Path2D.Double p2d = (refPath2D != null) ? refPath2D.get() : null; - - // create a new Path2D ? - if (p2d == null) { - p2d = new Path2D.Double(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K - - // update weak reference: - refPath2D = new WeakReference(p2d); - } - // reset the path anyway: - p2d.reset(); - return p2d; - } - - @Override - public RendererStats stats() { - return stats; - } - - @Override - public OffHeapArray newOffHeapArray(final long initialSize) { - if (DO_STATS) { - stats.totalOffHeapInitial += initialSize; - } - return new OffHeapArray(cleanerObj, initialSize); - } - - @Override - public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) { - return cleanIntCache.createRef(initialSize); - } - - ArrayCacheInt.Reference newDirtyIntArrayRef(final int initialSize) { - return dirtyIntCache.createRef(initialSize); - } - - ArrayCacheDouble.Reference newDirtyDoubleArrayRef(final int initialSize) { - return dirtyDoubleCache.createRef(initialSize); - } - - ArrayCacheByte.Reference newDirtyByteArrayRef(final int initialSize) { - return dirtyByteCache.createRef(initialSize); - } - - static final class PathConsumer2DAdapter implements DPathConsumer2D { - private sun.awt.geom.PathConsumer2D out; - - PathConsumer2DAdapter() {} - - PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) { - if (this.out != out) { - this.out = out; - } - return this; - } - - @Override - public void moveTo(double x0, double y0) { - out.moveTo((float)x0, (float)y0); - } - - @Override - public void lineTo(double x1, double y1) { - out.lineTo((float)x1, (float)y1); - } - - @Override - public void closePath() { - out.closePath(); - } - - @Override - public void pathDone() { - out.pathDone(); - } - - @Override - public void curveTo(double x1, double y1, - double x2, double y2, - double x3, double y3) - { - out.curveTo((float)x1, (float)y1, - (float)x2, (float)y2, - (float)x3, (float)y3); - } - - @Override - public void quadTo(double x1, double y1, double x2, double y2) { - out.quadTo((float)x1, (float)y1, (float)x2, (float)y2); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - } -} diff --git a/src/main/java/sun/java2d/marlin/DStroker.java b/src/main/java/sun/java2d/marlin/DStroker.java deleted file mode 100644 index 00a3880..0000000 --- a/src/main/java/sun/java2d/marlin/DStroker.java +++ /dev/null @@ -1,1388 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.util.Arrays; -import sun.java2d.marlin.DHelpers.PolyStack; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer; -import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter; -import sun.java2d.marlin.debug.MarlinDebugThreadLocal; - -// TODO: some of the arithmetic here is too verbose and prone to hard to -// debug typos. We should consider making a small Point/Vector class that -// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such -final class DStroker implements DPathConsumer2D, MarlinConst { - - private static final int MOVE_TO = 0; - private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad - private static final int CLOSE = 2; - - // round join threshold = 1 subpixel - private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS); - private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN; - - // kappa = (4/3) * (SQRT(2) - 1) - private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d); - - // SQRT(2) - private static final double SQRT_2 = Math.sqrt(2.0d); - - private DPathConsumer2D out; - - private int capStyle; - private int joinStyle; - - private double lineWidth2; - private double invHalfLineWidth2Sq; - - private final double[] offset0 = new double[2]; - private final double[] offset1 = new double[2]; - private final double[] offset2 = new double[2]; - private final double[] miter = new double[2]; - private double miterLimitSq; - - private int prev; - - // The starting point of the path, and the slope there. - private double sx0, sy0, sdx, sdy; - // the current point and the slope there. - private double cx0, cy0, cdx, cdy; // c stands for current - // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the - // first and last points on the left parallel path. Since this path is - // parallel, it's slope at any point is parallel to the slope of the - // original path (thought they may have different directions), so these - // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that - // would be error prone and hard to read, so we keep these anyway. - private double smx, smy, cmx, cmy; - - private final PolyStack reverse; - - private final double[] lp = new double[8]; - private final double[] rp = new double[8]; - - // per-thread renderer context - final DRendererContext rdrCtx; - - // dirty curve - final DCurve curve; - - // Bounds of the drawing region, at pixel precision. - private double[] clipRect; - - // the outcode of the current point - private int cOutCode = 0; - - // the outcode of the starting point - private int sOutCode = 0; - - // flag indicating if the path is opened (clipped) - private boolean opened = false; - // flag indicating if the starting point's cap is done - private boolean capStart = false; - // flag indicating to monotonize curves - private boolean monotonize; - - private boolean subdivide = false; - private final CurveClipSplitter curveSplitter; - - /** - * Constructs a DStroker. - * @param rdrCtx per-thread renderer context - */ - DStroker(final DRendererContext rdrCtx) { - this.rdrCtx = rdrCtx; - - this.reverse = (rdrCtx.stats != null) ? - new PolyStack(rdrCtx, - rdrCtx.stats.stat_str_polystack_types, - rdrCtx.stats.stat_str_polystack_curves, - rdrCtx.stats.hist_str_polystack_curves, - rdrCtx.stats.stat_array_str_polystack_curves, - rdrCtx.stats.stat_array_str_polystack_types) - : new PolyStack(rdrCtx); - - this.curve = rdrCtx.curve; - this.curveSplitter = rdrCtx.curveClipSplitter; - } - - /** - * Inits the DStroker. - * - * @param pc2d an output DPathConsumer2D. - * @param lineWidth the desired line width in pixels - * @param capStyle the desired end cap style, one of - * CAP_BUTT, CAP_ROUND or - * CAP_SQUARE. - * @param joinStyle the desired line join style, one of - * JOIN_MITER, JOIN_ROUND or - * JOIN_BEVEL. - * @param miterLimit the desired miter limit - * @param subdivideCurves true to indicate to subdivide curves, false if dasher does - * @return this instance - */ - DStroker init(final DPathConsumer2D pc2d, - final double lineWidth, - final int capStyle, - final int joinStyle, - final double miterLimit, - final boolean subdivideCurves) - { - if (this.out != pc2d) { - this.out = pc2d; - } - - this.lineWidth2 = lineWidth / 2.0d; - this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2); - this.monotonize = subdivideCurves; - - this.capStyle = capStyle; - this.joinStyle = joinStyle; - - final double limit = miterLimit * lineWidth2; - this.miterLimitSq = limit * limit; - - this.prev = CLOSE; - - rdrCtx.stroking = 1; - - if (rdrCtx.doClip) { - // Adjust the clipping rectangle with the stroker margin (miter limit, width) - double margin = lineWidth2; - - if (capStyle == CAP_SQUARE) { - margin *= SQRT_2; - } - if ((joinStyle == JOIN_MITER) && (margin < limit)) { - margin = limit; - } - - // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY - // adjust clip rectangle (ymin, ymax, xmin, xmax): - final double[] _clipRect = rdrCtx.clipRect; - _clipRect[0] -= margin; - _clipRect[1] += margin; - _clipRect[2] -= margin; - _clipRect[3] += margin; - this.clipRect = _clipRect; - - if (MarlinConst.DO_LOG_CLIP) { - MarlinUtils.logInfo("clipRect (stroker): " - + Arrays.toString(rdrCtx.clipRect)); - } - - // initialize curve splitter here for stroker & dasher: - if (DO_CLIP_SUBDIVIDER) { - subdivide = subdivideCurves; - // adjust padded clip rectangle: - curveSplitter.init(); - } else { - subdivide = false; - } - } else { - this.clipRect = null; - this.cOutCode = 0; - this.sOutCode = 0; - } - return this; // fluent API - } - - void disableClipping() { - this.clipRect = null; - this.cOutCode = 0; - this.sOutCode = 0; - } - - /** - * Disposes this stroker: - * clean up before reusing this instance - */ - void dispose() { - reverse.dispose(); - - opened = false; - capStart = false; - - if (DO_CLEAN_DIRTY) { - // Force zero-fill dirty arrays: - Arrays.fill(offset0, 0.0d); - Arrays.fill(offset1, 0.0d); - Arrays.fill(offset2, 0.0d); - Arrays.fill(miter, 0.0d); - Arrays.fill(lp, 0.0d); - Arrays.fill(rp, 0.0d); - } - } - - private static void computeOffset(final double lx, final double ly, - final double w, final double[] m) - { - double len = lx*lx + ly*ly; - if (len == 0.0d) { - m[0] = 0.0d; - m[1] = 0.0d; - } else { - len = Math.sqrt(len); - m[0] = (ly * w) / len; - m[1] = -(lx * w) / len; - } - } - - // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are - // clockwise (if dx1,dy1 needs to be rotated clockwise to close - // the smallest angle between it and dx2,dy2). - // This is equivalent to detecting whether a point q is on the right side - // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and - // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a - // clockwise order. - // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left. - private static boolean isCW(final double dx1, final double dy1, - final double dx2, final double dy2) - { - return dx1 * dy2 <= dy1 * dx2; - } - - private void mayDrawRoundJoin(double cx, double cy, - double omx, double omy, - double mx, double my, - boolean rev) - { - if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) { - return; - } - - final double domx = omx - mx; - final double domy = omy - my; - final double lenSq = domx*domx + domy*domy; - - if (lenSq < ROUND_JOIN_THRESHOLD) { - return; - } - - if (rev) { - omx = -omx; - omy = -omy; - mx = -mx; - my = -my; - } - drawRoundJoin(cx, cy, omx, omy, mx, my, rev); - } - - private void drawRoundJoin(double cx, double cy, - double omx, double omy, - double mx, double my, - boolean rev) - { - // The sign of the dot product of mx,my and omx,omy is equal to the - // the sign of the cosine of ext - // (ext is the angle between omx,omy and mx,my). - final double cosext = omx * mx + omy * my; - // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only - // need 1 curve to approximate the circle section that joins omx,omy - // and mx,my. - if (cosext >= 0.0d) { - drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev); - } else { - // we need to split the arc into 2 arcs spanning the same angle. - // The point we want will be one of the 2 intersections of the - // perpendicular bisector of the chord (omx,omy)->(mx,my) and the - // circle. We could find this by scaling the vector - // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies - // on the circle), but that can have numerical problems when the angle - // between omx,omy and mx,my is close to 180 degrees. So we compute a - // normal of (omx,omy)-(mx,my). This will be the direction of the - // perpendicular bisector. To get one of the intersections, we just scale - // this vector that its length is lineWidth2 (this works because the - // perpendicular bisector goes through the origin). This scaling doesn't - // have numerical problems because we know that lineWidth2 divided by - // this normal's length is at least 0.5 and at most sqrt(2)/2 (because - // we know the angle of the arc is > 90 degrees). - double nx = my - omy, ny = omx - mx; - double nlen = Math.sqrt(nx*nx + ny*ny); - double scale = lineWidth2/nlen; - double mmx = nx * scale, mmy = ny * scale; - - // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've - // computed the wrong intersection so we get the other one. - // The test above is equivalent to if (rev). - if (rev) { - mmx = -mmx; - mmy = -mmy; - } - drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev); - drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev); - } - } - - // the input arc defined by omx,omy and mx,my must span <= 90 degrees. - private void drawBezApproxForArc(final double cx, final double cy, - final double omx, final double omy, - final double mx, final double my, - boolean rev) - { - final double cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq; - - // check round off errors producing cos(ext) > 1 and a NaN below - // cos(ext) == 1 implies colinear segments and an empty join anyway - if (cosext2 >= 0.5d) { - // just return to avoid generating a flat curve: - return; - } - - // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc - // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that - // define the bezier curve we're computing. - // It is computed using the constraints that P1-P0 and P3-P2 are parallel - // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|. - double cv = ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) / - (1.0d + Math.sqrt(cosext2 + 0.5d))); - // if clockwise, we need to negate cv. - if (rev) { // rev is equivalent to isCW(omx, omy, mx, my) - cv = -cv; - } - final double x1 = cx + omx; - final double y1 = cy + omy; - final double x2 = x1 - cv * omy; - final double y2 = y1 + cv * omx; - - final double x4 = cx + mx; - final double y4 = cy + my; - final double x3 = x4 + cv * my; - final double y3 = y4 - cv * mx; - - emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev); - } - - private void drawRoundCap(double cx, double cy, double mx, double my) { - final double Cmx = C * mx; - final double Cmy = C * my; - emitCurveTo(cx + mx - Cmy, cy + my + Cmx, - cx - my + Cmx, cy + mx + Cmy, - cx - my, cy + mx); - emitCurveTo(cx - my - Cmx, cy + mx - Cmy, - cx - mx - Cmy, cy - my + Cmx, - cx - mx, cy - my); - } - - // Return the intersection point of the lines (x0, y0) -> (x1, y1) - // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] - private static void computeMiter(final double x0, final double y0, - final double x1, final double y1, - final double x0p, final double y0p, - final double x1p, final double y1p, - final double[] m) - { - double x10 = x1 - x0; - double y10 = y1 - y0; - double x10p = x1p - x0p; - double y10p = y1p - y0p; - - // if this is 0, the lines are parallel. If they go in the - // same direction, there is no intersection so m[off] and - // m[off+1] will contain infinity, so no miter will be drawn. - // If they go in the same direction that means that the start of the - // current segment and the end of the previous segment have the same - // tangent, in which case this method won't even be involved in - // miter drawing because it won't be called by drawMiter (because - // (mx == omx && my == omy) will be true, and drawMiter will return - // immediately). - double den = x10*y10p - x10p*y10; - double t = x10p*(y0-y0p) - y10p*(x0-x0p); - t /= den; - m[0] = x0 + t*x10; - m[1] = y0 + t*y10; - } - - // Return the intersection point of the lines (x0, y0) -> (x1, y1) - // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] - private static void safeComputeMiter(final double x0, final double y0, - final double x1, final double y1, - final double x0p, final double y0p, - final double x1p, final double y1p, - final double[] m) - { - double x10 = x1 - x0; - double y10 = y1 - y0; - double x10p = x1p - x0p; - double y10p = y1p - y0p; - - // if this is 0, the lines are parallel. If they go in the - // same direction, there is no intersection so m[off] and - // m[off+1] will contain infinity, so no miter will be drawn. - // If they go in the same direction that means that the start of the - // current segment and the end of the previous segment have the same - // tangent, in which case this method won't even be involved in - // miter drawing because it won't be called by drawMiter (because - // (mx == omx && my == omy) will be true, and drawMiter will return - // immediately). - double den = x10*y10p - x10p*y10; - if (den == 0.0d) { - m[2] = (x0 + x0p) / 2.0d; - m[3] = (y0 + y0p) / 2.0d; - } else { - double t = x10p*(y0-y0p) - y10p*(x0-x0p); - t /= den; - m[2] = x0 + t*x10; - m[3] = y0 + t*y10; - } - } - - private void drawMiter(final double pdx, final double pdy, - final double x0, final double y0, - final double dx, final double dy, - double omx, double omy, - double mx, double my, - boolean rev) - { - if ((mx == omx && my == omy) || - (pdx == 0.0d && pdy == 0.0d) || - (dx == 0.0d && dy == 0.0d)) - { - return; - } - - if (rev) { - omx = -omx; - omy = -omy; - mx = -mx; - my = -my; - } - - computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, - (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter); - - final double miterX = miter[0]; - final double miterY = miter[1]; - double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0); - - // If the lines are parallel, lenSq will be either NaN or +inf - // (actually, I'm not sure if the latter is possible. The important - // thing is that -inf is not possible, because lenSq is a square). - // For both of those values, the comparison below will fail and - // no miter will be drawn, which is correct. - if (lenSq < miterLimitSq) { - emitLineTo(miterX, miterY, rev); - } - } - - @Override - public void moveTo(final double x0, final double y0) { - _moveTo(x0, y0, cOutCode); - // update starting point: - this.sx0 = x0; - this.sy0 = y0; - this.sdx = 1.0d; - this.sdy = 0.0d; - this.opened = false; - this.capStart = false; - - if (clipRect != null) { - final int outcode = DHelpers.outcode(x0, y0, clipRect); - this.cOutCode = outcode; - this.sOutCode = outcode; - } - } - - private void _moveTo(final double x0, final double y0, - final int outcode) - { - if (prev == MOVE_TO) { - this.cx0 = x0; - this.cy0 = y0; - } else { - if (prev == DRAWING_OP_TO) { - finish(outcode); - } - this.prev = MOVE_TO; - this.cx0 = x0; - this.cy0 = y0; - this.cdx = 1.0d; - this.cdy = 0.0d; - } - } - - @Override - public void lineTo(final double x1, final double y1) { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, - orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode1; - _moveTo(x1, y1, outcode0); - opened = true; - return; - } - } - - this.cOutCode = outcode1; - } - - double dx = x1 - cx0; - double dy = y1 - cy0; - if (dx == 0.0d && dy == 0.0d) { - dx = 1.0d; - } - computeOffset(dx, dy, lineWidth2, offset0); - final double mx = offset0[0]; - final double my = offset0[1]; - - drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0); - - emitLineTo(cx0 + mx, cy0 + my); - emitLineTo( x1 + mx, y1 + my); - - emitLineToRev(cx0 - mx, cy0 - my); - emitLineToRev( x1 - mx, y1 - my); - - this.prev = DRAWING_OP_TO; - this.cx0 = x1; - this.cy0 = y1; - this.cdx = dx; - this.cdy = dy; - this.cmx = mx; - this.cmy = my; - } - - @Override - public void closePath() { - // distinguish empty path at all vs opened path ? - if (prev != DRAWING_OP_TO && !opened) { - if (prev == CLOSE) { - return; - } - emitMoveTo(cx0, cy0 - lineWidth2); - - this.sdx = 1.0d; - this.sdy = 0.0d; - this.cdx = 1.0d; - this.cdy = 0.0d; - - this.smx = 0.0d; - this.smy = -lineWidth2; - this.cmx = 0.0d; - this.cmy = -lineWidth2; - - finish(cOutCode); - return; - } - - // basic acceptance criteria - if ((sOutCode & cOutCode) == 0) { - if (cx0 != sx0 || cy0 != sy0) { - // may subdivide line: - lineTo(sx0, sy0); - } - - // ignore starting point outside: - if (sOutCode == 0) { - drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, sOutCode); - - emitLineTo(sx0 + smx, sy0 + smy); - - if (opened) { - emitLineTo(sx0 - smx, sy0 - smy); - } else { - emitMoveTo(sx0 - smx, sy0 - smy); - } - } - } - // Ignore caps like finish(false) - emitReverse(); - - this.prev = CLOSE; - this.cx0 = sx0; - this.cy0 = sy0; - this.cOutCode = sOutCode; - - if (opened) { - // do not emit close - opened = false; - } else { - emitClose(); - } - } - - private void emitReverse() { - reverse.popAll(out); - } - - @Override - public void pathDone() { - if (prev == DRAWING_OP_TO) { - finish(cOutCode); - } - - out.pathDone(); - - // this shouldn't matter since this object won't be used - // after the call to this method. - this.prev = CLOSE; - - // Dispose this instance: - dispose(); - } - - private void finish(final int outcode) { - // Problem: impossible to guess if the path will be closed in advance - // i.e. if caps must be drawn or not ? - // Solution: use the ClosedPathDetector before Stroker to determine - // if the path is a closed path or not - if (rdrCtx.closedPath) { - emitReverse(); - } else { - if (outcode == 0) { - // current point = end's cap: - if (capStyle == CAP_ROUND) { - drawRoundCap(cx0, cy0, cmx, cmy); - } else if (capStyle == CAP_SQUARE) { - emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy); - emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy); - } - } - emitReverse(); - - if (!capStart) { - capStart = true; - - if (sOutCode == 0) { - // starting point = initial cap: - if (capStyle == CAP_ROUND) { - drawRoundCap(sx0, sy0, -smx, -smy); - } else if (capStyle == CAP_SQUARE) { - emitLineTo(sx0 + smy - smx, sy0 - smx - smy); - emitLineTo(sx0 + smy + smx, sy0 - smx + smy); - } - } - } - } - emitClose(); - } - - private void emitMoveTo(final double x0, final double y0) { - out.moveTo(x0, y0); - } - - private void emitLineTo(final double x1, final double y1) { - out.lineTo(x1, y1); - } - - private void emitLineToRev(final double x1, final double y1) { - reverse.pushLine(x1, y1); - } - - private void emitLineTo(final double x1, final double y1, - final boolean rev) - { - if (rev) { - emitLineToRev(x1, y1); - } else { - emitLineTo(x1, y1); - } - } - - private void emitQuadTo(final double x1, final double y1, - final double x2, final double y2) - { - out.quadTo(x1, y1, x2, y2); - } - - private void emitQuadToRev(final double x0, final double y0, - final double x1, final double y1) - { - reverse.pushQuad(x0, y0, x1, y1); - } - - private void emitCurveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - out.curveTo(x1, y1, x2, y2, x3, y3); - } - - private void emitCurveToRev(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2) - { - reverse.pushCubic(x0, y0, x1, y1, x2, y2); - } - - private void emitCurveTo(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3, final boolean rev) - { - if (rev) { - reverse.pushCubic(x0, y0, x1, y1, x2, y2); - } else { - out.curveTo(x1, y1, x2, y2, x3, y3); - } - } - - private void emitClose() { - out.closePath(); - } - - private void drawJoin(double pdx, double pdy, - double x0, double y0, - double dx, double dy, - double omx, double omy, - double mx, double my, - final int outcode) - { - if (prev != DRAWING_OP_TO) { - emitMoveTo(x0 + mx, y0 + my); - if (!opened) { - this.sdx = dx; - this.sdy = dy; - this.smx = mx; - this.smy = my; - } - } else if (rdrCtx.isFirstSegment) { - // Precision on isCW is causing instabilities with Dasher ! - final boolean cw = isCW(pdx, pdy, dx, dy); - if (outcode == 0) { - if (joinStyle == JOIN_MITER) { - drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw); - } else if (joinStyle == JOIN_ROUND) { - mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw); - } - } - emitLineTo(x0, y0, !cw); - } - if (!rdrCtx.isFirstSegment) { - // reset trigger to process further joins (normal operations) - rdrCtx.isFirstSegment = true; - } - - prev = DRAWING_OP_TO; - } - - private int getLineOffsets(final double x1, final double y1, - final double x2, final double y2, - final double[] left, final double[] right) - { - computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0); - final double mx = offset0[0]; - final double my = offset0[1]; - left[0] = x1 + mx; - left[1] = y1 + my; - left[2] = x2 + mx; - left[3] = y2 + my; - - right[0] = x1 - mx; - right[1] = y1 - my; - right[2] = x2 - mx; - right[3] = y2 - my; - - return 4; - } - - private int computeOffsetCubic(final double[] pts, final int off, - final double[] leftOff, - final double[] rightOff) - { - // if p1=p2 or p3=p4 it means that the derivative at the endpoint - // vanishes, which creates problems with computeOffset. Usually - // this happens when this stroker object is trying to widen - // a curve with a cusp. What happens is that curveTo splits - // the input curve at the cusp, and passes it to this function. - // because of inaccuracies in the splitting, we consider points - // equal if they're very close to each other. - final double x1 = pts[off ]; final double y1 = pts[off + 1]; - final double x2 = pts[off + 2]; final double y2 = pts[off + 3]; - final double x3 = pts[off + 4]; final double y3 = pts[off + 5]; - final double x4 = pts[off + 6]; final double y4 = pts[off + 7]; - - double dx1 = x2 - x1; double dy1 = y2 - y1; - double dx4 = x4 - x3; double dy4 = y4 - y3; - - // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, - // in which case ignore if p1 == p2 - final boolean p1eqp2 = DHelpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2)); - final boolean p3eqp4 = DHelpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4)); - - if (p1eqp2 && p3eqp4) { - return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); - } else if (p1eqp2) { - dx1 = x3 - x1; - dy1 = y3 - y1; - } else if (p3eqp4) { - dx4 = x4 - x2; - dy4 = y4 - y2; - } - - // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line - double dotsq = (dx1 * dx4 + dy1 * dy4); - dotsq *= dotsq; - final double l1sq = dx1 * dx1 + dy1 * dy1; - final double l4sq = dx4 * dx4 + dy4 * dy4; - - if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) { - return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); - } - -// What we're trying to do in this function is to approximate an ideal -// offset curve (call it I) of the input curve B using a bezier curve Bp. -// The constraints I use to get the equations are: -// -// 1. The computed curve Bp should go through I(0) and I(1). These are -// x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find -// 4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p). -// -// 2. Bp should have slope equal in absolute value to I at the endpoints. So, -// (by the way, the operator || in the comments below means "aligned with". -// It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that -// vectors I'(0) and Bp'(0) are aligned, which is the same as saying -// that the tangent lines of I and Bp at 0 are parallel. Mathematically -// this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some -// nonzero constant.) -// I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and -// I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1). -// We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same -// is true for any bezier curve; therefore, we get the equations -// (1) p2p = c1 * (p2-p1) + p1p -// (2) p3p = c2 * (p4-p3) + p4p -// We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number -// of unknowns from 4 to 2 (i.e. just c1 and c2). -// To eliminate these 2 unknowns we use the following constraint: -// -// 3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note -// that I(0.5) is *the only* reason for computing dxm,dym. This gives us -// (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to -// (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3 -// We can substitute (1) and (2) from above into (4) and we get: -// (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p -// which is equivalent to -// (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p) -// -// The right side of this is a 2D vector, and we know I(0.5), which gives us -// Bp(0.5), which gives us the value of the right side. -// The left side is just a matrix vector multiplication in disguise. It is -// -// [x2-x1, x4-x3][c1] -// [y2-y1, y4-y3][c2] -// which, is equal to -// [dx1, dx4][c1] -// [dy1, dy4][c2] -// At this point we are left with a simple linear system and we solve it by -// getting the inverse of the matrix above. Then we use [c1,c2] to compute -// p2p and p3p. - - final double xm = (x1 + x4 + 3.0d * (x2 + x3)) / 8.0d; - final double ym = (y1 + y4 + 3.0d * (y2 + y3)) / 8.0d; - // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to - // c*B'(0.5) for some constant c. - final double dxm = x3 + x4 - (x1 + x2); - final double dym = y3 + y4 - (y1 + y2); - - // this computes the offsets at t=0, 0.5, 1, using the property that - // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to - // the (dx/dt, dy/dt) vectors at the endpoints. - computeOffset(dx1, dy1, lineWidth2, offset0); - computeOffset(dxm, dym, lineWidth2, offset1); - computeOffset(dx4, dy4, lineWidth2, offset2); - - // left side: - double x1p = x1 + offset0[0]; // start - double y1p = y1 + offset0[1]; // point - double xi = xm + offset1[0]; // interpolation - double yi = ym + offset1[1]; // point - double x4p = x4 + offset2[0]; // end - double y4p = y4 + offset2[1]; // point - -if (false) { - final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get(); - // never release (reset): - dbgCtx.addPoint(xi, yi); -} - - final double invdet43 = 4.0d / (3.0d * (dx1 * dy4 - dy1 * dx4)); - - double two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p); - double two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p); - - double c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); - double c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); - - double x2p, y2p, x3p, y3p; - - if (c1 * c2 > 0.0) { -// System.out.println("Buggy solver (left): c1 = " + c1 + " c2 = " + c2); - - // use lower quality approximation but good enough - // to ensure cuve being in its convex hull - x2p = x2 + offset1[0]; // 2nd - y2p = y2 + offset1[1]; // point - x3p = x3 + offset1[0]; // 3nd - y3p = y3 + offset1[1]; // point - - safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x2p, y2p, x2p-dxm, y2p-dym, leftOff); - x2p = leftOff[2]; y2p = leftOff[3]; - - safeComputeMiter(x4p, y4p, x4p+dx4, y4p+dy4, x3p, y3p, x3p-dxm, y3p-dym, leftOff); - x3p = leftOff[2]; y3p = leftOff[3]; - } else { - x2p = x1p + c1 * dx1; y2p = y1p + c1 * dy1; - x3p = x4p + c2 * dx4; y3p = y4p + c2 * dy4; - } - - leftOff[0] = x1p; leftOff[1] = y1p; - leftOff[2] = x2p; leftOff[3] = y2p; - leftOff[4] = x3p; leftOff[5] = y3p; - leftOff[6] = x4p; leftOff[7] = y4p; - - // Right side: - x1p = x1 - offset0[0]; // start - y1p = y1 - offset0[1]; // point - xi = xm - offset1[0]; // interpolation - yi = ym - offset1[1]; // point - x4p = x4 - offset2[0]; // end - y4p = y4 - offset2[1]; // point - -if (false) { - final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get(); - // never release (reset): - dbgCtx.addPoint(xi, yi); -} - - two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p); - two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p); - - c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); - c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); - - if (c1 * c2 > 0.0) { -// System.out.println("Buggy solver (right): c1 = " + c1 + " c2 = " + c2); - - // use lower quality approximation but good enough - // to ensure cuve being in its convex hull - x2p = x2 - offset1[0]; // 2nd - y2p = y2 - offset1[1]; // point - x3p = x3 - offset1[0]; // 3nd - y3p = y3 - offset1[1]; // point - - safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x2p, y2p, x2p-dxm, y2p-dym, rightOff); - x2p = rightOff[2]; y2p = rightOff[3]; - - safeComputeMiter(x4p, y4p, x4p+dx4, y4p+dy4, x3p, y3p, x3p-dxm, y3p-dym, rightOff); - x3p = rightOff[2]; y3p = rightOff[3]; - } else { - x2p = x1p + c1 * dx1; y2p = y1p + c1 * dy1; - x3p = x4p + c2 * dx4; y3p = y4p + c2 * dy4; - } - - rightOff[0] = x1p; rightOff[1] = y1p; - rightOff[2] = x2p; rightOff[3] = y2p; - rightOff[4] = x3p; rightOff[5] = y3p; - rightOff[6] = x4p; rightOff[7] = y4p; - - return 8; - } - - // compute offset curves using bezier spline through t=0.5 (i.e. - // ComputedCurve(0.5) == IdealParallelCurve(0.5)) - // return the kind of curve in the right and left arrays. - private int computeOffsetQuad(final double[] pts, final int off, - final double[] leftOff, - final double[] rightOff) - { - return computeOffsetQuad(pts, off, leftOff, rightOff, true); - } - - private int computeOffsetQuad(final double[] pts, final int off, - final double[] leftOff, - final double[] rightOff, - final boolean checkCtrlPoints) - { - final double x1 = pts[off ]; final double y1 = pts[off + 1]; - final double x2 = pts[off + 2]; final double y2 = pts[off + 3]; - final double x3 = pts[off + 4]; final double y3 = pts[off + 5]; - - final double dx12 = x2 - x1; final double dy12 = y2 - y1; - final double dx23 = x3 - x2; final double dy23 = y3 - y2; - - if (checkCtrlPoints) { - // if p1=p2 or p2=p3 it means that the derivative at the endpoint - // vanishes, which creates problems with computeOffset. Usually - // this happens when this stroker object is trying to widen - // a curve with a cusp. What happens is that curveTo splits - // the input curve at the cusp, and passes it to this function. - // because of inaccuracies in the splitting, we consider points - // equal if they're very close to each other. - - // if p1 == p2 or p2 == p3: draw line from p1->p3 - final boolean p1eqp2 = DHelpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2)); - final boolean p2eqp3 = DHelpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3)); - - if (p1eqp2 || p2eqp3) { - return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); - } - - // if p2-p1 and p3-p2 are parallel, that must mean this curve is a line - double dotsq = (dx12 * dx23 + dy12 * dy23); - dotsq *= dotsq; - final double l1sq = dx12 * dx12 + dy12 * dy12; - final double l3sq = dx23 * dx23 + dy23 * dy23; - - if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) { - return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); - } - } - - // this computes the offsets at t=0, 0.5, 1, using the property that - // for any bezier curve the vectors p2-p1 and p3-p2 are parallel to - // the (dx/dt, dy/dt) vectors at the endpoints. - computeOffset(dx12, dy12, lineWidth2, offset0); - computeOffset(dx23, dy23, lineWidth2, offset1); - - double x1p = x1 + offset0[0]; // start - double y1p = y1 + offset0[1]; // point - double x3p = x3 + offset1[0]; // end - double y3p = y3 + offset1[1]; // point - - safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, leftOff); - leftOff[0] = x1p; leftOff[1] = y1p; - leftOff[4] = x3p; leftOff[5] = y3p; - - x1p = x1 - offset0[0]; // start - y1p = y1 - offset0[1]; // point - x3p = x3 - offset1[0]; // end - y3p = y3 - offset1[1]; // point - - safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, rightOff); - rightOff[0] = x1p; rightOff[1] = y1p; - rightOff[4] = x3p; rightOff[5] = y3p; - - return 6; - } - - @Override - public void curveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(x2, y2, clipRect); - final int outcode3 = DHelpers.outcode(x3, y3, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2 | outcode3); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, - x2, y2, x3, y3, - orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode3; - _moveTo(x3, y3, outcode0); - opened = true; - return; - } - } - - this.cOutCode = outcode3; - } - _curveTo(x1, y1, x2, y2, x3, y3, outcode0); - } - - private void _curveTo(final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3, - final int outcode0) - { - // need these so we can update the state at the end of this method - double dxs = x1 - cx0; - double dys = y1 - cy0; - double dxf = x3 - x2; - double dyf = y3 - y2; - - if ((dxs == 0.0d) && (dys == 0.0d)) { - dxs = x2 - cx0; - dys = y2 - cy0; - if ((dxs == 0.0d) && (dys == 0.0d)) { - dxs = x3 - cx0; - dys = y3 - cy0; - } - } - if ((dxf == 0.0d) && (dyf == 0.0d)) { - dxf = x3 - x1; - dyf = y3 - y1; - if ((dxf == 0.0d) && (dyf == 0.0d)) { - dxf = x3 - cx0; - dyf = y3 - cy0; - } - } - if ((dxs == 0.0d) && (dys == 0.0d)) { - // this happens if the "curve" is just a point - // fix outcode0 for lineTo() call: - if (clipRect != null) { - this.cOutCode = outcode0; - } - lineTo(cx0, cy0); - return; - } - - // if these vectors are too small, normalize them, to avoid future - // precision problems. - if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { - final double len = Math.sqrt(dxs * dxs + dys * dys); - dxs /= len; - dys /= len; - } - if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { - final double len = Math.sqrt(dxf * dxf + dyf * dyf); - dxf /= len; - dyf /= len; - } - - computeOffset(dxs, dys, lineWidth2, offset0); - drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); - - int nSplits = 0; - final double[] mid; - final double[] l = lp; - - if (monotonize) { - // monotonize curve: - final CurveBasicMonotonizer monotonizer - = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3); - - nSplits = monotonizer.nbSplits; - mid = monotonizer.middle; - } else { - // use left instead: - mid = l; - mid[0] = cx0; mid[1] = cy0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - mid[6] = x3; mid[7] = y3; - } - final double[] r = rp; - - int kind = 0; - for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { - kind = computeOffsetCubic(mid, off, l, r); - - emitLineTo(l[0], l[1]); - - switch(kind) { - case 8: - emitCurveTo(l[2], l[3], l[4], l[5], l[6], l[7]); - emitCurveToRev(r[0], r[1], r[2], r[3], r[4], r[5]); - break; - case 4: - emitLineTo(l[2], l[3]); - emitLineToRev(r[0], r[1]); - break; - default: - } - emitLineToRev(r[kind - 2], r[kind - 1]); - } - - this.prev = DRAWING_OP_TO; - this.cx0 = x3; - this.cy0 = y3; - this.cdx = dxf; - this.cdy = dyf; - this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; - this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; - } - - @Override - public void quadTo(final double x1, final double y1, - final double x2, final double y2) - { - final int outcode0 = this.cOutCode; - - if (clipRect != null) { - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(x2, y2, clipRect); - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => call lineTo() with subdivided curves: - boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, - x2, y2, orCode, this); - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode2; - _moveTo(x2, y2, outcode0); - opened = true; - return; - } - } - - this.cOutCode = outcode2; - } - _quadTo(x1, y1, x2, y2, outcode0); - } - - private void _quadTo(final double x1, final double y1, - final double x2, final double y2, - final int outcode0) - { - // need these so we can update the state at the end of this method - double dxs = x1 - cx0; - double dys = y1 - cy0; - double dxf = x2 - x1; - double dyf = y2 - y1; - - if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) { - dxs = dxf = x2 - cx0; - dys = dyf = y2 - cy0; - } - if ((dxs == 0.0d) && (dys == 0.0d)) { - // this happens if the "curve" is just a point - // fix outcode0 for lineTo() call: - if (clipRect != null) { - this.cOutCode = outcode0; - } - lineTo(cx0, cy0); - return; - } - // if these vectors are too small, normalize them, to avoid future - // precision problems. - if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { - final double len = Math.sqrt(dxs * dxs + dys * dys); - dxs /= len; - dys /= len; - } - if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { - final double len = Math.sqrt(dxf * dxf + dyf * dyf); - dxf /= len; - dyf /= len; - } - computeOffset(dxs, dys, lineWidth2, offset0); - drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); - - int nSplits = 0; - final double[] mid; - final double[] l = lp; - - if (monotonize) { - // monotonize quad: - final CurveBasicMonotonizer monotonizer - = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2); - - nSplits = monotonizer.nbSplits; - mid = monotonizer.middle; - } else { - // use left instead: - mid = l; - mid[0] = cx0; mid[1] = cy0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - } - final double[] r = rp; - - int kind = 0; - for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { - kind = computeOffsetQuad(mid, off, l, r); - - emitLineTo(l[0], l[1]); - - switch(kind) { - case 6: - emitQuadTo(l[2], l[3], l[4], l[5]); - emitQuadToRev(r[0], r[1], r[2], r[3]); - break; - case 4: - emitLineTo(l[2], l[3]); - emitLineToRev(r[0], r[1]); - break; - default: - } - emitLineToRev(r[kind - 2], r[kind - 1]); - } - - this.prev = DRAWING_OP_TO; - this.cx0 = x2; - this.cy0 = y2; - this.cdx = dxf; - this.cdy = dyf; - this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; - this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; - } - - @Override public long getNativeConsumer() { - throw new InternalError("Stroker doesn't use a native consumer"); - } -} diff --git a/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java b/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java deleted file mode 100644 index d4d9b7e..0000000 --- a/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java +++ /dev/null @@ -1,1389 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; -import java.util.Arrays; -import sun.java2d.marlin.DHelpers.IndexStack; -import sun.java2d.marlin.DHelpers.PolyStack; - -final class DTransformingPathConsumer2D { - - // smaller uncertainty in double variant - static final double CLIP_RECT_PADDING = 0.25d; - - private final DRendererContext rdrCtx; - - // recycled ClosedPathDetector instance from detectClosedPath() - private final ClosedPathDetector cpDetector; - - // recycled PathClipFilter instance from pathClipper() - private final PathClipFilter pathClipper; - - // recycled DPathConsumer2D instance from wrapPath2D() - private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); - - // recycled DPathConsumer2D instances from deltaTransformConsumer() - private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); - private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); - - // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer() - private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); - private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); - - // recycled PathTracer instances from tracer...() methods - private final PathTracer tracerInput = new PathTracer("[Input]"); - private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector"); - private final PathTracer tracerFiller = new PathTracer("Filler"); - private final PathTracer tracerStroker = new PathTracer("Stroker"); - private final PathTracer tracerDasher = new PathTracer("Dasher"); - - DTransformingPathConsumer2D(final DRendererContext rdrCtx) { - // used by RendererContext - this.rdrCtx = rdrCtx; - this.cpDetector = new ClosedPathDetector(rdrCtx); - this.pathClipper = new PathClipFilter(rdrCtx); - } - - DPathConsumer2D wrapPath2D(Path2D.Double p2d) { - return wp_Path2DWrapper.init(p2d); - } - - DPathConsumer2D traceInput(DPathConsumer2D out) { - return tracerInput.init(out); - } - - DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) { - return tracerCPDetector.init(out); - } - - DPathConsumer2D traceFiller(DPathConsumer2D out) { - return tracerFiller.init(out); - } - - DPathConsumer2D traceStroker(DPathConsumer2D out) { - return tracerStroker.init(out); - } - - DPathConsumer2D traceDasher(DPathConsumer2D out) { - return tracerDasher.init(out); - } - - DPathConsumer2D detectClosedPath(DPathConsumer2D out) { - return cpDetector.init(out); - } - - DPathConsumer2D pathClipper(DPathConsumer2D out) { - return pathClipper.init(out); - } - - DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, - AffineTransform at) - { - if (at == null) { - return out; - } - final double mxx = at.getScaleX(); - final double mxy = at.getShearX(); - final double myx = at.getShearY(); - final double myy = at.getScaleY(); - - if (mxy == 0.0d && myx == 0.0d) { - if (mxx == 1.0d && myy == 1.0d) { - return out; - } else { - // Scale only - if (rdrCtx.doClip) { - // adjust clip rectangle (ymin, ymax, xmin, xmax): - rdrCtx.clipInvScale = adjustClipScale(rdrCtx.clipRect, - mxx, myy); - } - return dt_DeltaScaleFilter.init(out, mxx, myy); - } - } else { - if (rdrCtx.doClip) { - // adjust clip rectangle (ymin, ymax, xmin, xmax): - rdrCtx.clipInvScale = adjustClipInverseDelta(rdrCtx.clipRect, - mxx, mxy, myx, myy); - } - return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); - } - } - - private static double adjustClipScale(final double[] clipRect, - final double mxx, final double myy) - { - // Adjust the clipping rectangle (iv_DeltaScaleFilter): - final double scaleY = 1.0d / myy; - clipRect[0] *= scaleY; - clipRect[1] *= scaleY; - - if (clipRect[1] < clipRect[0]) { - double tmp = clipRect[0]; - clipRect[0] = clipRect[1]; - clipRect[1] = tmp; - } - - final double scaleX = 1.0d / mxx; - clipRect[2] *= scaleX; - clipRect[3] *= scaleX; - - if (clipRect[3] < clipRect[2]) { - double tmp = clipRect[2]; - clipRect[2] = clipRect[3]; - clipRect[3] = tmp; - } - - if (MarlinConst.DO_LOG_CLIP) { - MarlinUtils.logInfo("clipRect (ClipScale): " - + Arrays.toString(clipRect)); - } - return 0.5d * (Math.abs(scaleX) + Math.abs(scaleY)); - } - - private static double adjustClipInverseDelta(final double[] clipRect, - final double mxx, final double mxy, - final double myx, final double myy) - { - // Adjust the clipping rectangle (iv_DeltaTransformFilter): - final double det = mxx * myy - mxy * myx; - final double imxx = myy / det; - final double imxy = -mxy / det; - final double imyx = -myx / det; - final double imyy = mxx / det; - - double xmin, xmax, ymin, ymax; - double x, y; - // xmin, ymin: - x = clipRect[2] * imxx + clipRect[0] * imxy; - y = clipRect[2] * imyx + clipRect[0] * imyy; - - xmin = xmax = x; - ymin = ymax = y; - - // xmax, ymin: - x = clipRect[3] * imxx + clipRect[0] * imxy; - y = clipRect[3] * imyx + clipRect[0] * imyy; - - if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } - if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } - - // xmin, ymax: - x = clipRect[2] * imxx + clipRect[1] * imxy; - y = clipRect[2] * imyx + clipRect[1] * imyy; - - if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } - if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } - - // xmax, ymax: - x = clipRect[3] * imxx + clipRect[1] * imxy; - y = clipRect[3] * imyx + clipRect[1] * imyy; - - if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } - if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } - - clipRect[0] = ymin; - clipRect[1] = ymax; - clipRect[2] = xmin; - clipRect[3] = xmax; - - if (MarlinConst.DO_LOG_CLIP) { - MarlinUtils.logInfo("clipRect (ClipInverseDelta): " - + Arrays.toString(clipRect)); - } - - final double scaleX = Math.sqrt(imxx * imxx + imxy * imxy); - final double scaleY = Math.sqrt(imyx * imyx + imyy * imyy); - - return 0.5d * (scaleX + scaleY); - } - - DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, - AffineTransform at) - { - if (at == null) { - return out; - } - double mxx = at.getScaleX(); - double mxy = at.getShearX(); - double myx = at.getShearY(); - double myy = at.getScaleY(); - - if (mxy == 0.0d && myx == 0.0d) { - if (mxx == 1.0d && myy == 1.0d) { - return out; - } else { - return iv_DeltaScaleFilter.init(out, 1.0d / mxx, 1.0d / myy); - } - } else { - final double det = mxx * myy - mxy * myx; - return iv_DeltaTransformFilter.init(out, - myy / det, - -mxy / det, - -myx / det, - mxx / det); - } - } - - static final class DeltaScaleFilter implements DPathConsumer2D { - private DPathConsumer2D out; - private double sx, sy; - - DeltaScaleFilter() {} - - DeltaScaleFilter init(DPathConsumer2D out, - double mxx, double myy) - { - if (this.out != out) { - this.out = out; - } - sx = mxx; - sy = myy; - return this; // fluent API - } - - @Override - public void moveTo(double x0, double y0) { - out.moveTo(x0 * sx, y0 * sy); - } - - @Override - public void lineTo(double x1, double y1) { - out.lineTo(x1 * sx, y1 * sy); - } - - @Override - public void quadTo(double x1, double y1, - double x2, double y2) - { - out.quadTo(x1 * sx, y1 * sy, - x2 * sx, y2 * sy); - } - - @Override - public void curveTo(double x1, double y1, - double x2, double y2, - double x3, double y3) - { - out.curveTo(x1 * sx, y1 * sy, - x2 * sx, y2 * sy, - x3 * sx, y3 * sy); - } - - @Override - public void closePath() { - out.closePath(); - } - - @Override - public void pathDone() { - out.pathDone(); - } - - @Override - public long getNativeConsumer() { - return 0; - } - } - - static final class DeltaTransformFilter implements DPathConsumer2D { - private DPathConsumer2D out; - private double mxx, mxy, myx, myy; - - DeltaTransformFilter() {} - - DeltaTransformFilter init(DPathConsumer2D out, - double mxx, double mxy, - double myx, double myy) - { - if (this.out != out) { - this.out = out; - } - this.mxx = mxx; - this.mxy = mxy; - this.myx = myx; - this.myy = myy; - return this; // fluent API - } - - @Override - public void moveTo(double x0, double y0) { - out.moveTo(x0 * mxx + y0 * mxy, - x0 * myx + y0 * myy); - } - - @Override - public void lineTo(double x1, double y1) { - out.lineTo(x1 * mxx + y1 * mxy, - x1 * myx + y1 * myy); - } - - @Override - public void quadTo(double x1, double y1, - double x2, double y2) - { - out.quadTo(x1 * mxx + y1 * mxy, - x1 * myx + y1 * myy, - x2 * mxx + y2 * mxy, - x2 * myx + y2 * myy); - } - - @Override - public void curveTo(double x1, double y1, - double x2, double y2, - double x3, double y3) - { - out.curveTo(x1 * mxx + y1 * mxy, - x1 * myx + y1 * myy, - x2 * mxx + y2 * mxy, - x2 * myx + y2 * myy, - x3 * mxx + y3 * mxy, - x3 * myx + y3 * myy); - } - - @Override - public void closePath() { - out.closePath(); - } - - @Override - public void pathDone() { - out.pathDone(); - } - - @Override - public long getNativeConsumer() { - return 0; - } - } - - static final class Path2DWrapper implements DPathConsumer2D { - private Path2D.Double p2d; - - Path2DWrapper() {} - - Path2DWrapper init(Path2D.Double p2d) { - if (this.p2d != p2d) { - this.p2d = p2d; - } - return this; - } - - @Override - public void moveTo(double x0, double y0) { - p2d.moveTo(x0, y0); - } - - @Override - public void lineTo(double x1, double y1) { - p2d.lineTo(x1, y1); - } - - @Override - public void closePath() { - p2d.closePath(); - } - - @Override - public void pathDone() {} - - @Override - public void curveTo(double x1, double y1, - double x2, double y2, - double x3, double y3) - { - p2d.curveTo(x1, y1, x2, y2, x3, y3); - } - - @Override - public void quadTo(double x1, double y1, double x2, double y2) { - p2d.quadTo(x1, y1, x2, y2); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - } - - static final class ClosedPathDetector implements DPathConsumer2D { - - private final DRendererContext rdrCtx; - private final PolyStack stack; - - private DPathConsumer2D out; - - ClosedPathDetector(final DRendererContext rdrCtx) { - this.rdrCtx = rdrCtx; - this.stack = (rdrCtx.stats != null) ? - new PolyStack(rdrCtx, - rdrCtx.stats.stat_cpd_polystack_types, - rdrCtx.stats.stat_cpd_polystack_curves, - rdrCtx.stats.hist_cpd_polystack_curves, - rdrCtx.stats.stat_array_cpd_polystack_curves, - rdrCtx.stats.stat_array_cpd_polystack_types) - : new PolyStack(rdrCtx); - } - - ClosedPathDetector init(DPathConsumer2D out) { - if (this.out != out) { - this.out = out; - } - return this; // fluent API - } - - /** - * Disposes this instance: - * clean up before reusing this instance - */ - void dispose() { - stack.dispose(); - } - - @Override - public void pathDone() { - // previous path is not closed: - finish(false); - out.pathDone(); - - // TODO: fix possible leak if exception happened - // Dispose this instance: - dispose(); - } - - @Override - public void closePath() { - // path is closed - finish(true); - out.closePath(); - } - - @Override - public void moveTo(double x0, double y0) { - // previous path is not closed: - finish(false); - out.moveTo(x0, y0); - } - - private void finish(final boolean closed) { - rdrCtx.closedPath = closed; - stack.pullAll(out); - } - - @Override - public void lineTo(double x1, double y1) { - stack.pushLine(x1, y1); - } - - @Override - public void curveTo(double x3, double y3, - double x2, double y2, - double x1, double y1) - { - stack.pushCubic(x1, y1, x2, y2, x3, y3); - } - - @Override - public void quadTo(double x2, double y2, double x1, double y1) { - stack.pushQuad(x1, y1, x2, y2); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - } - - static final class PathClipFilter implements DPathConsumer2D { - - private static final boolean TRACE = false; - - private static final int MOVE_TO = 0; - private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad - private static final int CLOSE = 2; - - private DPathConsumer2D out; - - private int prev; - - // Bounds of the drawing region, at pixel precision. - private final double[] clipRect; - - private final double[] corners = new double[8]; - private boolean init_corners = false; - - private final IndexStack stack; - - // the current outcode of the current sub path - private int cOutCode = 0; - - // the outcode of the starting point - private int sOutCode = 0; - - // the cumulated (and) outcode of the complete path - private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R; - - private boolean outside = false; - - // The starting point of the path - private double sx0, sy0; - - // The current point (TODO stupid repeated info) - private double cx0, cy0; - - // The current point OUTSIDE - private double cox0, coy0; - - private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER; - private final CurveClipSplitter curveSplitter; - - PathClipFilter(final DRendererContext rdrCtx) { - this.clipRect = rdrCtx.clipRect; - this.curveSplitter = rdrCtx.curveClipSplitter; - - this.stack = (rdrCtx.stats != null) ? - new IndexStack(rdrCtx, - rdrCtx.stats.stat_pcf_idxstack_indices, - rdrCtx.stats.hist_pcf_idxstack_indices, - rdrCtx.stats.stat_array_pcf_idxstack_indices) - : new IndexStack(rdrCtx); - } - - PathClipFilter init(final DPathConsumer2D out) { - if (this.out != out) { - this.out = out; - } - - if (MarlinConst.DO_CLIP_SUBDIVIDER) { - // adjust padded clip rectangle: - curveSplitter.init(); - } - - this.init_corners = true; - this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R; - this.prev = CLOSE; - - return this; // fluent API - } - - /** - * Disposes this instance: - * clean up before reusing this instance - */ - void dispose() { - stack.dispose(); - } - - private void finishPath() { - if (outside) { - // criteria: inside or totally outside ? - if (gOutCode == 0) { - finish(); - } else { - this.outside = false; - stack.reset(); - } - } - } - - private void finish() { - this.outside = false; - - if (!stack.isEmpty()) { - if (init_corners) { - init_corners = false; - - final double[] _corners = corners; - final double[] _clipRect = clipRect; - // Top Left (0): - _corners[0] = _clipRect[2]; - _corners[1] = _clipRect[0]; - // Bottom Left (1): - _corners[2] = _clipRect[2]; - _corners[3] = _clipRect[1]; - // Top right (2): - _corners[4] = _clipRect[3]; - _corners[5] = _clipRect[0]; - // Bottom Right (3): - _corners[6] = _clipRect[3]; - _corners[7] = _clipRect[1]; - } - stack.pullAll(corners, out, (prev == MOVE_TO)); - prev = DRAWING_OP_TO; - } - // go to the last outside point: - this.cx0 = cox0; - this.cy0 = coy0; - } - - @Override - public void pathDone() { - if (TRACE) { - System.out.println("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev); - } - _closePath(); - - out.pathDone(); - - // this shouldn't matter since this object won't be used - // after the call to this method. - this.prev = CLOSE; - - // TODO: fix possible leak if exception happened - // Dispose this instance: - dispose(); - } - - @Override - public void closePath() { - if (TRACE) { - System.out.println("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev); - } - _closePath(); - - out.closePath(); - - // if outside, moveTo is needed - if (sOutCode != 0) { - this.prev = MOVE_TO; - } else { - this.prev = CLOSE; - } - - // back to starting point: - this.cOutCode = sOutCode; - this.cx0 = sx0; - this.cy0 = sy0; - } - - private void _closePath() { - // preserve outside flag for the lineTo call below - final boolean prevOutside = outside; - finishPath(); - - if (prev == DRAWING_OP_TO) { - // Should clip - final int orCode = (cOutCode | sOutCode); - if (orCode != 0) { - if (cx0 != sx0 || cy0 != sy0) { - // restore outside flag before lineTo: - this.outside = prevOutside; - // may subdivide line: - lineTo(sx0, sy0); - } - } - } - finishPath(); - } - - @Override - public void moveTo(final double x0, final double y0) { - if (TRACE) { - System.out.println("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev); - } - _closePath(); - - this.prev = MOVE_TO; - - // update starting point: - final int outcode = DHelpers.outcode(x0, y0, clipRect); - this.cOutCode = outcode; - this.sOutCode = outcode; - this.cx0 = x0; - this.cy0 = y0; - - this.sx0 = x0; - this.sy0 = y0; - } - - @Override - public void lineTo(final double xe, final double ye) { - final int outcode0 = this.cOutCode; - final int outcode1 = DHelpers.outcode(xe, ye, clipRect); - - if (TRACE) { - if (subdivide) { - System.out.println("----------------------"); - } - if (outside) { - System.out.println("LineTo co (" + cox0 + ", " + coy0 + ")"); - } - System.out.println("LineTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); - } - - // Should clip - final int orCode = (outcode0 | outcode1); - if (orCode != 0) { - final int sideCode = (outcode0 & outcode1); - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - boolean ret; - // subdivide curve => callback with subdivided parts: - if (outside) { - ret = curveSplitter.splitLine(cox0, coy0, xe, ye, - orCode, this); - } else { - ret = curveSplitter.splitLine(cx0, cy0, xe, ye, - orCode, this); - } - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode1; - this.gOutCode &= sideCode; - // keep last point coordinate before entering the clip again: - this.outside = true; - this.cox0 = xe; - this.coy0 = ye; - - if (TRACE) { - System.out.println("skipped: (" + cox0 + ", " + coy0 + ")"); - } - - clip(sideCode, outcode0, outcode1); - return; - } - } - - this.cOutCode = outcode1; - this.gOutCode = 0; - - if (outside) { - finish(); - - // emit last point outside before entering again... - if (outcode0 != 0) { - if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); - } - if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); - } else { - out.lineTo(cox0, coy0); - } - prev = DRAWING_OP_TO; - } - } - // clipping disabled: - if (prev == MOVE_TO) { - out.moveTo(cx0, cy0); - } - prev = DRAWING_OP_TO; - - out.lineTo(xe, ye); - this.cx0 = xe; - this.cy0 = ye; - - if (TRACE && subdivide) { - System.out.println("----------------------"); - } - } - - private void clip(final int sideCode, - final int outcode0, - final int outcode1) - { - // corner or cross-boundary on left or right side: - if ((outcode0 != outcode1) - && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0)) - { - // combine outcodes: - final int mergeCode = (outcode0 | outcode1); - final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B; - final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R; - final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2; - - // add corners to outside stack: - switch (tbCode) { - case MarlinConst.OUTCODE_TOP: - stack.push(off); // top - return; - case MarlinConst.OUTCODE_BOTTOM: - stack.push(off + 1); // bottom - return; - default: - // both TOP / BOTTOM: - if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) { - // top to bottom - stack.push(off); // top - stack.push(off + 1); // bottom - } else { - // bottom to top - stack.push(off + 1); // bottom - stack.push(off); // top - } - } - } - } - - @Override - public void curveTo(final double x1, final double y1, - final double x2, final double y2, - final double xe, final double ye) - { - final int outcode0 = this.cOutCode; - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(x2, y2, clipRect); - final int outcode3 = DHelpers.outcode(xe, ye, clipRect); - - if (TRACE) { - if (subdivide) { - System.out.println("----------------------"); - } - if (outside) { - System.out.println("CurveTo co (" + cox0 + ", " + coy0 + ")"); - } - System.out.println("CurveTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside); - } - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2 | outcode3); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret; - if (outside) { - ret = curveSplitter.splitCurve(cox0, coy0, x1, y1, - x2, y2, xe, ye, - orCode, this); - } else { - ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, - x2, y2, xe, ye, - orCode, this); - } - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode3; - this.gOutCode &= sideCode; - // keep last point coordinate before entering the clip again: - this.outside = true; - this.cox0 = xe; - this.coy0 = ye; - - if (TRACE) { - System.out.println("skipped: (" + cox0 + ", " + coy0 + ")"); - } - - clip(sideCode, outcode0, outcode3); - return; - } - } - - this.cOutCode = outcode3; - this.gOutCode = 0; - - if (outside) { - finish(); - - // emit last point outside before entering again... - if (outcode0 != 0) { - if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); - } - if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); - } else { - out.lineTo(cox0, coy0); - } - prev = DRAWING_OP_TO; - } - } - // clipping disabled: - if (prev == MOVE_TO) { - out.moveTo(cx0, cy0); - } - prev = DRAWING_OP_TO; - - out.curveTo(x1, y1, x2, y2, xe, ye); - this.cx0 = xe; - this.cy0 = ye; - - if (TRACE && subdivide) { - System.out.println("----------------------"); - } - } - - @Override - public void quadTo(final double x1, final double y1, - final double xe, final double ye) - { - final int outcode0 = this.cOutCode; - final int outcode1 = DHelpers.outcode(x1, y1, clipRect); - final int outcode2 = DHelpers.outcode(xe, ye, clipRect); - - if (TRACE) { - if (subdivide) { - System.out.println("----------------------"); - } - if (outside) { - System.out.println("QuadTo co (" + cox0 + ", " + coy0 + ")"); - } - System.out.println("QuadTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); - } - - // Should clip - final int orCode = (outcode0 | outcode1 | outcode2); - if (orCode != 0) { - final int sideCode = outcode0 & outcode1 & outcode2; - - // basic rejection criteria: - if (sideCode == 0) { - // overlap clip: - if (subdivide) { - // avoid reentrance - subdivide = false; - // subdivide curve => callback with subdivided parts: - boolean ret; - if (outside) { - ret = curveSplitter.splitQuad(cox0, coy0, x1, y1, - xe, ye, orCode, this); - } else { - ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, - xe, ye, orCode, this); - } - // reentrance is done: - subdivide = true; - if (ret) { - return; - } - } - // already subdivided so render it - } else { - this.cOutCode = outcode2; - this.gOutCode &= sideCode; - // keep last point coordinate before entering the clip again: - this.outside = true; - this.cox0 = xe; - this.coy0 = ye; - - clip(sideCode, outcode0, outcode2); - return; - } - } - - this.cOutCode = outcode2; - this.gOutCode = 0; - - if (outside) { - finish(); - - // emit last point outside before entering again... - if (outcode0 != 0) { - if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); - } - if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); - } else { - out.lineTo(cox0, coy0); - } - prev = DRAWING_OP_TO; - } - } - // clipping disabled: - if (prev == MOVE_TO) { - out.moveTo(cx0, cy0); - } - prev = DRAWING_OP_TO; - - out.quadTo(x1, y1, xe, ye); - this.cx0 = xe; - this.cy0 = ye; - - if (TRACE && subdivide) { - System.out.println("----------------------"); - } - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - } - - static final class CurveClipSplitter { - - static final double LEN_TH = MarlinProperties.getSubdividerMinLength(); - static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d); - - private static final boolean TRACE = false; - - private static final int MAX_N_CURVES = 3 * 4; - - private final DRendererContext rdrCtx; - - // scaled length threshold: - private double minLength; - - // clip rectangle (ymin, ymax, xmin, xmax): - final double[] clipRect; - - // clip rectangle (ymin, ymax, xmin, xmax) including padding: - final double[] clipRectPad = new double[4]; - private boolean init_clipRectPad = false; - - // This is where the curve to be processed is put. We give it - // enough room to store all curves. - final double[] middle = new double[MAX_N_CURVES * 8 + 2]; - // t values at subdivision points - private final double[] subdivTs = new double[MAX_N_CURVES]; - - // dirty curve - private final DCurve curve; - - CurveClipSplitter(final DRendererContext rdrCtx) { - this.rdrCtx = rdrCtx; - this.clipRect = rdrCtx.clipRect; - this.curve = rdrCtx.curve; - } - - void init() { - this.init_clipRectPad = true; - - if (DO_CHECK_LENGTH) { - this.minLength = (this.rdrCtx.clipInvScale == 0.0d) ? LEN_TH - : (LEN_TH * this.rdrCtx.clipInvScale); - - if (MarlinConst.DO_LOG_CLIP) { - MarlinUtils.logInfo("CurveClipSplitter.minLength = " - + minLength); - } - } - } - - private void initPaddedClip() { - // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY - // adjust padded clip rectangle (ymin, ymax, xmin, xmax): - // add a rounding error (curve subdivision ~ 0.1px): - final double[] _clipRect = clipRect; - final double[] _clipRectPad = clipRectPad; - - _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING; - _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING; - _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING; - _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING; - - if (TRACE) { - MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] " - + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]"); - } - } - - boolean splitLine(final double x0, final double y0, - final double x1, final double y1, - final int outCodeOR, - final DPathConsumer2D out) - { - if (TRACE) { - MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")"); - } - - if (DO_CHECK_LENGTH && DHelpers.fastLineLen(x0, y0, x1, y1) <= minLength) { - return false; - } - - final double[] mid = middle; - mid[0] = x0; mid[1] = y0; - mid[2] = x1; mid[3] = y1; - - return subdivideAtIntersections(4, outCodeOR, out); - } - - boolean splitQuad(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final int outCodeOR, - final DPathConsumer2D out) - { - if (TRACE) { - MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")"); - } - - if (DO_CHECK_LENGTH && DHelpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= minLength) { - return false; - } - - final double[] mid = middle; - mid[0] = x0; mid[1] = y0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - - return subdivideAtIntersections(6, outCodeOR, out); - } - - boolean splitCurve(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3, - final int outCodeOR, - final DPathConsumer2D out) - { - if (TRACE) { - MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")"); - } - - if (DO_CHECK_LENGTH && DHelpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= minLength) { - return false; - } - - final double[] mid = middle; - mid[0] = x0; mid[1] = y0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - mid[6] = x3; mid[7] = y3; - - return subdivideAtIntersections(8, outCodeOR, out); - } - - private boolean subdivideAtIntersections(final int type, final int outCodeOR, - final DPathConsumer2D out) - { - final double[] mid = middle; - final double[] subTs = subdivTs; - - if (init_clipRectPad) { - init_clipRectPad = false; - initPaddedClip(); - } - - final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type, - outCodeOR, clipRectPad); - - if (TRACE) { - MarlinUtils.logInfo("nSplits: " + nSplits); - MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits))); - } - if (nSplits == 0) { - // only curve support shortcut - return false; - } - double prevT = 0.0d; - - for (int i = 0, off = 0; i < nSplits; i++, off += type) { - final double t = subTs[i]; - - DHelpers.subdivideAt((t - prevT) / (1.0d - prevT), - mid, off, mid, off, type); - prevT = t; - } - - for (int i = 0, off = 0; i <= nSplits; i++, off += type) { - if (TRACE) { - MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type))); - } - emitCurrent(type, mid, off, out); - } - return true; - } - - static void emitCurrent(final int type, final double[] pts, - final int off, final DPathConsumer2D out) - { - // if instead of switch (perf + most probable cases first) - if (type == 8) { - out.curveTo(pts[off + 2], pts[off + 3], - pts[off + 4], pts[off + 5], - pts[off + 6], pts[off + 7]); - } else if (type == 4) { - out.lineTo(pts[off + 2], pts[off + 3]); - } else { - out.quadTo(pts[off + 2], pts[off + 3], - pts[off + 4], pts[off + 5]); - } - } - } - - static final class CurveBasicMonotonizer { - - private static final int MAX_N_CURVES = 11; - - // squared half line width (for stroker) - private double lw2; - - // number of splitted curves - int nbSplits; - - // This is where the curve to be processed is put. We give it - // enough room to store all curves. - final double[] middle = new double[MAX_N_CURVES * 6 + 2]; - // t values at subdivision points - private final double[] subdivTs = new double[MAX_N_CURVES - 1]; - - // dirty curve - private final DCurve curve; - - CurveBasicMonotonizer(final DRendererContext rdrCtx) { - this.curve = rdrCtx.curve; - } - - void init(final double lineWidth) { - this.lw2 = (lineWidth * lineWidth) / 4.0d; - } - - CurveBasicMonotonizer curve(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2, - final double x3, final double y3) - { - final double[] mid = middle; - mid[0] = x0; mid[1] = y0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - mid[6] = x3; mid[7] = y3; - - final double[] subTs = subdivTs; - final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 8, lw2); - - double prevT = 0.0d; - for (int i = 0, off = 0; i < nSplits; i++, off += 6) { - final double t = subTs[i]; - - DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT), - mid, off, mid, off, off + 6); - prevT = t; - } - - this.nbSplits = nSplits; - return this; - } - - CurveBasicMonotonizer quad(final double x0, final double y0, - final double x1, final double y1, - final double x2, final double y2) - { - final double[] mid = middle; - mid[0] = x0; mid[1] = y0; - mid[2] = x1; mid[3] = y1; - mid[4] = x2; mid[5] = y2; - - final double[] subTs = subdivTs; - final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 6, lw2); - - double prevt = 0.0d; - for (int i = 0, off = 0; i < nSplits; i++, off += 4) { - final double t = subTs[i]; - DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt), - mid, off, mid, off, off + 4); - prevt = t; - } - - this.nbSplits = nSplits; - return this; - } - } - - static final class PathTracer implements DPathConsumer2D { - private final String prefix; - private DPathConsumer2D out; - - PathTracer(String name) { - this.prefix = name + ": "; - } - - PathTracer init(DPathConsumer2D out) { - if (this.out != out) { - this.out = out; - } - return this; // fluent API - } - - @Override - public void moveTo(double x0, double y0) { - log("p.moveTo(" + x0 + ", " + y0 + ");"); - out.moveTo(x0, y0); - } - - @Override - public void lineTo(double x1, double y1) { - log("p.lineTo(" + x1 + ", " + y1 + ");"); - out.lineTo(x1, y1); - } - - @Override - public void curveTo(double x1, double y1, - double x2, double y2, - double x3, double y3) - { - log("p.curveTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ", " + x3 + ", " + y3 + ");"); - out.curveTo(x1, y1, x2, y2, x3, y3); - } - - @Override - public void quadTo(double x1, double y1, - double x2, double y2) { - log("p.quadTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ");"); - out.quadTo(x1, y1, x2, y2); - } - - @Override - public void closePath() { - log("p.closePath();"); - out.closePath(); - } - - @Override - public void pathDone() { - log("p.pathDone();"); - out.pathDone(); - } - - private void log(final String message) { - MarlinUtils.logInfo(prefix + message); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - } -} diff --git a/src/main/java/sun/java2d/marlin/Dasher.java b/src/main/java/sun/java2d/marlin/Dasher.java index cfcb208..aa75a29 100644 --- a/src/main/java/sun/java2d/marlin/Dasher.java +++ b/src/main/java/sun/java2d/marlin/Dasher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.util.Arrays; import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer; import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter; -import sun.awt.geom.PathConsumer2D; /** * The Dasher class takes a series of linear commands @@ -41,24 +40,24 @@ * semantics are unclear. * */ -final class Dasher implements PathConsumer2D, MarlinConst { +final class Dasher implements DPathConsumer2D, MarlinConst { /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */ static final int REC_LIMIT = 16; - static final float CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 - static final float MIN_T_INC = 1.0f / (1 << REC_LIMIT); + static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial + static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT); - static final float EPS = 1e-6f; + static final double EPS = 1e-6d; // More than 24 bits of mantissa means we can no longer accurately // measure the number of times cycled through the dash array so we // punt and override the phase to just be 0 past that point. - static final float MAX_CYCLES = 16000000.0f; + static final double MAX_CYCLES = 16000000.0d; - private PathConsumer2D out; - private float[] dash; + private DPathConsumer2D out; + private double[] dash; private int dashLen; - private float startPhase; + private double startPhase; private boolean startDashOn; private int startIdx; @@ -67,15 +66,15 @@ final class Dasher implements PathConsumer2D, MarlinConst { private int idx; private boolean dashOn; - private float phase; + private double phase; // The starting point of the path - private float sx0, sy0; + private double sx0, sy0; // the current point - private float cx0, cy0; + private double cx0, cy0; // temporary storage for the current curve - private final float[] curCurvepts; + private final double[] curCurvepts; // per-thread renderer context final RendererContext rdrCtx; @@ -87,16 +86,16 @@ final class Dasher implements PathConsumer2D, MarlinConst { // drawn on it, but we need joins to be drawn if there's a closePath() // So, we store the path elements that make up the first dash in the // buffer below. - private float[] firstSegmentsBuffer; // dynamic array + private double[] firstSegmentsBuffer; // dynamic array private int firstSegidx; // dashes ref (dirty) - final ArrayCacheFloat.Reference dashes_ref; + final ArrayCacheDouble.Reference dashes_ref; // firstSegmentsBuffer ref (dirty) - final ArrayCacheFloat.Reference firstSegmentsBuffer_ref; + final ArrayCacheDouble.Reference firstSegmentsBuffer_ref; // Bounds of the drawing region, at pixel precision. - private float[] clipRect; + private double[] clipRect; // the outcode of the current point private int cOutCode = 0; @@ -107,9 +106,9 @@ final class Dasher implements PathConsumer2D, MarlinConst { private final CurveClipSplitter curveSplitter; - private float cycleLen; + private double cycleLen; private boolean outside; - private float totalSkipLen; + private double totalSkipLen; /** * Constructs a Dasher. @@ -118,14 +117,14 @@ final class Dasher implements PathConsumer2D, MarlinConst { Dasher(final RendererContext rdrCtx) { this.rdrCtx = rdrCtx; - dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K + dashes_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K - firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K + firstSegmentsBuffer_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K firstSegmentsBuffer = firstSegmentsBuffer_ref.initial; // we need curCurvepts to be able to contain 2 curves because when // dashing curves, we need to subdivide it - curCurvepts = new float[8 * 2]; + curCurvepts = new double[8 * 2]; this.curveSplitter = rdrCtx.curveClipSplitter; } @@ -133,15 +132,15 @@ final class Dasher implements PathConsumer2D, MarlinConst { /** * Initialize the Dasher. * - * @param out an output PathConsumer2D. - * @param dash an array of floats containing the dash pattern + * @param out an output DPathConsumer2D. + * @param dash an array of doubles containing the dash pattern * @param dashLen length of the given dash array - * @param phase a float containing the dash phase + * @param phase a double containing the dash phase * @param recycleDashes true to indicate to recycle the given dash array * @return this instance */ - Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, - float phase, final boolean recycleDashes) + Dasher init(final DPathConsumer2D out, final double[] dash, final int dashLen, + double phase, final boolean recycleDashes) { if (this.out != out) { this.out = out; @@ -152,23 +151,23 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, dashOn = true; // note: BasicStroke constructor checks dash elements and sum > 0 - float sum = 0.0f; + double sum = 0.0d; for (int i = 0; i < dashLen; i++) { sum += dash[i]; } this.cycleLen = sum; - float cycles = phase / sum; - if (phase < 0.0f) { + double cycles = phase / sum; + if (phase < 0.0d) { if (-cycles >= MAX_CYCLES) { - phase = 0.0f; + phase = 0.0d; } else { int fullcycles = FloatMath.floor_int(-cycles); if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase += fullcycles * sum; - while (phase < 0.0f) { + while (phase < 0.0d) { if (--sidx < 0) { sidx = dashLen - 1; } @@ -176,16 +175,16 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, dashOn = !dashOn; } } - } else if (phase > 0.0f) { + } else if (phase > 0.0d) { if (cycles >= MAX_CYCLES) { - phase = 0.0f; + phase = 0.0d; } else { int fullcycles = FloatMath.floor_int(cycles); if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase -= fullcycles * sum; - float d; + double d; while (phase >= (d = dash[sidx])) { phase -= d; sidx = (sidx + 1) % dashLen; @@ -222,7 +221,7 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, void dispose() { if (DO_CLEAN_DIRTY) { // Force zero-fill dirty arrays: - Arrays.fill(curCurvepts, 0.0f); + Arrays.fill(curCurvepts, 0.0d); } // Return arrays: if (recycleDashes) { @@ -235,9 +234,9 @@ void dispose() { } } - float[] copyDashArray(final float[] dashes) { + double[] copyDashArray(final float[] dashes) { final int len = dashes.length; - final float[] newDashes; + final double[] newDashes; if (len <= MarlinConst.INITIAL_ARRAY) { newDashes = dashes_ref.initial; } else { @@ -246,12 +245,12 @@ float[] copyDashArray(final float[] dashes) { } newDashes = dashes_ref.getArray(len); } - System.arraycopy(dashes, 0, newDashes, 0, len); + for (int i = 0; i < len; i++) { newDashes[i] = dashes[i]; } return newDashes; } @Override - public void moveTo(final float x0, final float y0) { + public void moveTo(final double x0, final double y0) { if (firstSegidx != 0) { out.moveTo(sx0, sy0); emitFirstSegments(); @@ -272,11 +271,11 @@ public void moveTo(final float x0, final float y0) { final int outcode = Helpers.outcode(x0, y0, clipRect); this.cOutCode = outcode; this.outside = false; - this.totalSkipLen = 0.0f; + this.totalSkipLen = 0.0d; } } - private void emitSeg(float[] buf, int off, int type) { + private void emitSeg(double[] buf, int off, int type) { switch (type) { case 4: out.lineTo(buf[off], buf[off + 1]); @@ -295,7 +294,7 @@ private void emitSeg(float[] buf, int off, int type) { } private void emitFirstSegments() { - final float[] fSegBuf = firstSegmentsBuffer; + final double[] fSegBuf = firstSegmentsBuffer; for (int i = 0, len = firstSegidx; i < len; ) { int type = (int)fSegBuf[i]; @@ -306,12 +305,12 @@ private void emitFirstSegments() { } // precondition: pts must be in relative coordinates (relative to x0,y0) - private void goTo(final float[] pts, final int off, final int type, + private void goTo(final double[] pts, final int off, final int type, final boolean on) { final int index = off + type; - final float x = pts[index - 4]; - final float y = pts[index - 3]; + final double x = pts[index - 4]; + final double y = pts[index - 3]; if (on) { if (starting) { @@ -334,10 +333,10 @@ private void goTo(final float[] pts, final int off, final int type, this.cy0 = y; } - private void goTo_starting(final float[] pts, final int off, final int type) { + private void goTo_starting(final double[] pts, final int off, final int type) { int len = type - 1; // - 2 + 1 int segIdx = firstSegidx; - float[] buf = firstSegmentsBuffer; + double[] buf = firstSegmentsBuffer; if (segIdx + len > buf.length) { if (DO_STATS) { @@ -356,7 +355,7 @@ private void goTo_starting(final float[] pts, final int off, final int type) { } @Override - public void lineTo(final float x1, final float y1) { + public void lineTo(final double x1, final double y1) { final int outcode0 = this.cOutCode; if (clipRect != null) { @@ -402,30 +401,30 @@ public void lineTo(final float x1, final float y1) { _lineTo(x1, y1); } - private void _lineTo(final float x1, final float y1) { - final float dx = x1 - cx0; - final float dy = y1 - cy0; + private void _lineTo(final double x1, final double y1) { + final double dx = x1 - cx0; + final double dy = y1 - cy0; - float len = dx * dx + dy * dy; - if (len == 0.0f) { + double len = dx * dx + dy * dy; + if (len == 0.0d) { return; } - len = (float) Math.sqrt(len); + len = Math.sqrt(len); // The scaling factors needed to get the dx and dy of the // transformed dash segments. - final float cx = dx / len; - final float cy = dy / len; + final double cx = dx / len; + final double cy = dy / len; - final float[] _curCurvepts = curCurvepts; - final float[] _dash = dash; + final double[] _curCurvepts = curCurvepts; + final double[] _dash = dash; final int _dashLen = this.dashLen; int _idx = idx; boolean _dashOn = dashOn; - float _phase = phase; + double _phase = phase; - float leftInThisDashSegment, rem; + double leftInThisDashSegment, rem; while (true) { leftInThisDashSegment = _dash[_idx] - _phase; @@ -442,7 +441,7 @@ private void _lineTo(final float x1, final float y1) { // compare values using epsilon: if (Math.abs(rem) <= EPS) { - _phase = 0.0f; + _phase = 0.0d; _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; } @@ -458,7 +457,7 @@ private void _lineTo(final float x1, final float y1) { // Advance to next dash segment _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; - _phase = 0.0f; + _phase = 0.0d; } // Save local state: idx = _idx; @@ -466,13 +465,13 @@ private void _lineTo(final float x1, final float y1) { phase = _phase; } - private void skipLineTo(final float x1, final float y1) { - final float dx = x1 - cx0; - final float dy = y1 - cy0; + private void skipLineTo(final double x1, final double y1) { + final double dx = x1 - cx0; + final double dy = y1 - cy0; - float len = dx * dx + dy * dy; - if (len != 0.0f) { - len = (float)Math.sqrt(len); + double len = dx * dx + dy * dy; + if (len != 0.0d) { + len = Math.sqrt(len); } // Accumulate skipped length: @@ -488,15 +487,15 @@ private void skipLineTo(final float x1, final float y1) { } public void skipLen() { - float len = this.totalSkipLen; - this.totalSkipLen = 0.0f; + double len = this.totalSkipLen; + this.totalSkipLen = 0.0d; - final float[] _dash = dash; + final double[] _dash = dash; final int _dashLen = this.dashLen; int _idx = idx; boolean _dashOn = dashOn; - float _phase = phase; + double _phase = phase; // -2 to ensure having 2 iterations of the post-loop // to compensate the remaining phase @@ -510,7 +509,7 @@ public void skipLen() { _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L; } - float leftInThisDashSegment, rem; + double leftInThisDashSegment, rem; while (true) { leftInThisDashSegment = _dash[_idx] - _phase; @@ -522,7 +521,7 @@ public void skipLen() { // compare values using epsilon: if (Math.abs(rem) <= EPS) { - _phase = 0.0f; + _phase = 0.0d; _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; } @@ -533,7 +532,7 @@ public void skipLen() { // Advance to next dash segment _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; - _phase = 0.0f; + _phase = 0.0d; } // Save local state: idx = _idx; @@ -544,29 +543,29 @@ public void skipLen() { // preconditions: curCurvepts must be an array of length at least 2 * type, // that contains the curve we want to dash in the first type elements private void somethingTo(final int type) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; if (Helpers.isPointCurve(_curCurvepts, type)) { return; } final LengthIterator _li = li; - final float[] _dash = dash; + final double[] _dash = dash; final int _dashLen = this.dashLen; _li.initializeIterationOnCurve(_curCurvepts, type); int _idx = idx; boolean _dashOn = dashOn; - float _phase = phase; + double _phase = phase; // initially the current curve is at curCurvepts[0...type] int curCurveoff = 0; - float prevT = 0.0f; - float t; - float leftInThisDashSegment = _dash[_idx] - _phase; + double prevT = 0.0d; + double t; + double leftInThisDashSegment = _dash[_idx] - _phase; - while ((t = _li.next(leftInThisDashSegment)) < 1.0f) { - if (t != 0.0f) { - Helpers.subdivideAt((t - prevT) / (1.0f - prevT), + while ((t = _li.next(leftInThisDashSegment)) < 1.0d) { + if (t != 0.0d) { + Helpers.subdivideAt((t - prevT) / (1.0d - prevT), _curCurvepts, curCurveoff, _curCurvepts, 0, type); prevT = t; @@ -576,7 +575,7 @@ private void somethingTo(final int type) { // Advance to next dash segment _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; - _phase = 0.0f; + _phase = 0.0d; leftInThisDashSegment = _dash[_idx]; } @@ -586,7 +585,7 @@ private void somethingTo(final int type) { // compare values using epsilon: if (_phase + EPS >= _dash[_idx]) { - _phase = 0.0f; + _phase = 0.0d; _idx = (_idx + 1) % _dashLen; _dashOn = !_dashOn; } @@ -600,7 +599,7 @@ private void somethingTo(final int type) { } private void skipSomethingTo(final int type) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; if (Helpers.isPointCurve(_curCurvepts, type)) { return; } @@ -610,7 +609,7 @@ private void skipSomethingTo(final int type) { // In contrary to somethingTo(), // just estimate properly the curve length: - final float len = _li.totalLength(); + final double len = _li.totalLength(); // Accumulate skipped length: this.outside = true; @@ -639,18 +638,18 @@ static final class LengthIterator { // (i.e. the original curve) is at recCurveStack[0] (but then it // gets subdivided, the left half is put at 1, so most of the time // only the right half of the original curve is at 0) - private final float[][] recCurveStack; // dirty + private final double[][] recCurveStack; // dirty // sidesRight[i] indicates whether the node at level i+1 in the path from // the root to the current leaf is a left or right child of its parent. private final boolean[] sidesRight; // dirty private int curveType; // lastT and nextT delimit the current leaf. - private float nextT; - private float lenAtNextT; - private float lastT; - private float lenAtLastT; - private float lenAtLastSplit; - private float lastSegLen; + private double nextT; + private double lenAtNextT; + private double lastT; + private double lenAtLastT; + private double lenAtLastSplit; + private double lastSegLen; // the current level in the recursion tree. 0 is the root. limit // is the deepest possible leaf. private int recLevel; @@ -659,18 +658,18 @@ static final class LengthIterator { // the lengths of the lines of the control polygon. Only its first // curveType/2 - 1 elements are valid. This is an optimization. See // next() for more detail. - private final float[] curLeafCtrlPolyLengths = new float[3]; + private final double[] curLeafCtrlPolyLengths = new double[3]; LengthIterator() { - this.recCurveStack = new float[REC_LIMIT + 1][8]; + this.recCurveStack = new double[REC_LIMIT + 1][8]; this.sidesRight = new boolean[REC_LIMIT]; // if any methods are called without first initializing this object // on a curve, we want it to fail ASAP. - this.nextT = Float.MAX_VALUE; - this.lenAtNextT = Float.MAX_VALUE; - this.lenAtLastSplit = Float.MIN_VALUE; + this.nextT = Double.MAX_VALUE; + this.lenAtNextT = Double.MAX_VALUE; + this.lenAtLastSplit = Double.MIN_VALUE; this.recLevel = Integer.MIN_VALUE; - this.lastSegLen = Float.MAX_VALUE; + this.lastSegLen = Double.MAX_VALUE; this.done = true; } @@ -683,29 +682,29 @@ void reset() { if (DO_CLEAN_DIRTY) { final int recLimit = recCurveStack.length - 1; for (int i = recLimit; i >= 0; i--) { - Arrays.fill(recCurveStack[i], 0.0f); + Arrays.fill(recCurveStack[i], 0.0d); } Arrays.fill(sidesRight, false); - Arrays.fill(curLeafCtrlPolyLengths, 0.0f); - Arrays.fill(nextRoots, 0.0f); - Arrays.fill(flatLeafCoefCache, 0.0f); - flatLeafCoefCache[2] = -1.0f; + Arrays.fill(curLeafCtrlPolyLengths, 0.0d); + Arrays.fill(nextRoots, 0.0d); + Arrays.fill(flatLeafCoefCache, 0.0d); + flatLeafCoefCache[2] = -1.0d; } } - void initializeIterationOnCurve(final float[] pts, final int type) { + void initializeIterationOnCurve(final double[] pts, final int type) { // optimize arraycopy (8 values faster than 6 = type): System.arraycopy(pts, 0, recCurveStack[0], 0, 8); this.curveType = type; this.recLevel = 0; - this.lastT = 0.0f; - this.lenAtLastT = 0.0f; - this.nextT = 0.0f; - this.lenAtNextT = 0.0f; + this.lastT = 0.0d; + this.lenAtLastT = 0.0d; + this.nextT = 0.0d; + this.lenAtNextT = 0.0d; // initializes nextT and lenAtNextT properly goLeft(); - this.lenAtLastSplit = 0.0f; + this.lenAtLastSplit = 0.0d; if (recLevel > 0) { this.sidesRight[0] = false; this.done = false; @@ -714,16 +713,16 @@ void initializeIterationOnCurve(final float[] pts, final int type) { this.sidesRight[0] = true; this.done = true; } - this.lastSegLen = 0.0f; + this.lastSegLen = 0.0d; } // 0 == false, 1 == true, -1 == invalid cached value. private int cachedHaveLowAcceleration = -1; - private boolean haveLowAcceleration(final float err) { + private boolean haveLowAcceleration(final double err) { if (cachedHaveLowAcceleration == -1) { - final float len1 = curLeafCtrlPolyLengths[0]; - final float len2 = curLeafCtrlPolyLengths[1]; + final double len1 = curLeafCtrlPolyLengths[0]; + final double len2 = curLeafCtrlPolyLengths[1]; // the test below is equivalent to !within(len1/len2, 1, err). // It is using a multiplication instead of a division, so it // should be a bit faster. @@ -732,11 +731,11 @@ private boolean haveLowAcceleration(final float err) { return false; } if (curveType == 8) { - final float len3 = curLeafCtrlPolyLengths[2]; + final double len3 = curLeafCtrlPolyLengths[2]; // if len1 is close to 2 and 2 is close to 3, that probably // means 1 is close to 3 so the second part of this test might // not be needed, but it doesn't hurt to include it. - final float errLen3 = err * len3; + final double errLen3 = err * len3; if (!(Helpers.within(len2, len3, errLen3) && Helpers.within(len1, len3, errLen3))) { cachedHaveLowAcceleration = 0; @@ -752,73 +751,73 @@ private boolean haveLowAcceleration(final float err) { // we want to avoid allocations/gc so we keep this array so we // can put roots in it, - private final float[] nextRoots = new float[4]; + private final double[] nextRoots = new double[4]; // caches the coefficients of the current leaf in its flattened // form (see inside next() for what that means). The cache is // invalid when it's third element is negative, since in any // valid flattened curve, this would be >= 0. - private final float[] flatLeafCoefCache = new float[]{0.0f, 0.0f, -1.0f, 0.0f}; + private final double[] flatLeafCoefCache = new double[]{0.0d, 0.0d, -1.0d, 0.0d}; // returns the t value where the remaining curve should be split in // order for the left subdivided curve to have length len. If len // is >= than the length of the uniterated curve, it returns 1. - float next(final float len) { - final float targetLength = lenAtLastSplit + len; + double next(final double len) { + final double targetLength = lenAtLastSplit + len; while (lenAtNextT < targetLength) { if (done) { lastSegLen = lenAtNextT - lenAtLastSplit; - return 1.0f; + return 1.0d; } goToNextLeaf(); } lenAtLastSplit = targetLength; - final float leaflen = lenAtNextT - lenAtLastT; - float t = (targetLength - lenAtLastT) / leaflen; + final double leaflen = lenAtNextT - lenAtLastT; + double t = (targetLength - lenAtLastT) / leaflen; // cubicRootsInAB is a fairly expensive call, so we just don't do it // if the acceleration in this section of the curve is small enough. - if (!haveLowAcceleration(0.05f)) { + if (!haveLowAcceleration(0.05d)) { // We flatten the current leaf along the x axis, so that we're // left with a, b, c which define a 1D Bezier curve. We then // solve this to get the parameter of the original leaf that // gives us the desired length. - final float[] _flatLeafCoefCache = flatLeafCoefCache; + final double[] _flatLeafCoefCache = flatLeafCoefCache; - if (_flatLeafCoefCache[2] < 0.0f) { - float x = curLeafCtrlPolyLengths[0], - y = x + curLeafCtrlPolyLengths[1]; + if (_flatLeafCoefCache[2] < 0.0d) { + double x = curLeafCtrlPolyLengths[0], + y = x + curLeafCtrlPolyLengths[1]; if (curveType == 8) { - float z = y + curLeafCtrlPolyLengths[2]; - _flatLeafCoefCache[0] = 3.0f * (x - y) + z; - _flatLeafCoefCache[1] = 3.0f * (y - 2.0f * x); - _flatLeafCoefCache[2] = 3.0f * x; + double z = y + curLeafCtrlPolyLengths[2]; + _flatLeafCoefCache[0] = 3.0d * (x - y) + z; + _flatLeafCoefCache[1] = 3.0d * (y - 2.0d * x); + _flatLeafCoefCache[2] = 3.0d * x; _flatLeafCoefCache[3] = -z; } else if (curveType == 6) { - _flatLeafCoefCache[0] = 0.0f; - _flatLeafCoefCache[1] = y - 2.0f * x; - _flatLeafCoefCache[2] = 2.0f * x; + _flatLeafCoefCache[0] = 0.0d; + _flatLeafCoefCache[1] = y - 2.0d * x; + _flatLeafCoefCache[2] = 2.0d * x; _flatLeafCoefCache[3] = -y; } } - float a = _flatLeafCoefCache[0]; - float b = _flatLeafCoefCache[1]; - float c = _flatLeafCoefCache[2]; - float d = t * _flatLeafCoefCache[3]; + double a = _flatLeafCoefCache[0]; + double b = _flatLeafCoefCache[1]; + double c = _flatLeafCoefCache[2]; + double d = t * _flatLeafCoefCache[3]; // we use cubicRootsInAB here, because we want only roots in 0, 1, // and our quadratic root finder doesn't filter, so it's just a // matter of convenience. - final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f); - if (n == 1 && !Float.isNaN(nextRoots[0])) { + final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d); + if (n == 1 && !Double.isNaN(nextRoots[0])) { t = nextRoots[0]; } } // t is relative to the current leaf, so we must make it a valid parameter // of the original curve. t = t * (nextT - lastT) + lastT; - if (t >= 1.0f) { - t = 1.0f; + if (t >= 1.0d) { + t = 1.0d; done = true; } // even if done = true, if we're here, that means targetLength @@ -830,7 +829,7 @@ float next(final float len) { return t; } - float totalLength() { + double totalLength() { while (!done) { goToNextLeaf(); } @@ -840,7 +839,7 @@ float totalLength() { return lenAtNextT; } - float lastSegLen() { + double lastSegLen() { return lastSegLen; } @@ -872,19 +871,19 @@ private void goToNextLeaf() { // go to the leftmost node from the current node. Return its length. private void goLeft() { - final float len = onLeaf(); - if (len >= 0.0f) { + final double len = onLeaf(); + if (len >= 0.0d) { lastT = nextT; lenAtLastT = lenAtNextT; nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC; lenAtNextT += len; // invalidate caches - flatLeafCoefCache[2] = -1.0f; + flatLeafCoefCache[2] = -1.0d; cachedHaveLowAcceleration = -1; } else { Helpers.subdivide(recCurveStack[recLevel], - recCurveStack[recLevel + 1], - recCurveStack[recLevel], curveType); + recCurveStack[recLevel + 1], + recCurveStack[recLevel], curveType); sidesRight[recLevel] = false; recLevel++; @@ -894,34 +893,34 @@ private void goLeft() { // this is a bit of a hack. It returns -1 if we're not on a leaf, and // the length of the leaf if we are on a leaf. - private float onLeaf() { - final float[] curve = recCurveStack[recLevel]; + private double onLeaf() { + final double[] curve = recCurveStack[recLevel]; final int _curveType = curveType; - float polyLen = 0.0f; + double polyLen = 0.0d; - float x0 = curve[0], y0 = curve[1]; + double x0 = curve[0], y0 = curve[1]; for (int i = 2; i < _curveType; i += 2) { - final float x1 = curve[i], y1 = curve[i + 1]; - final float len = Helpers.linelen(x0, y0, x1, y1); + final double x1 = curve[i], y1 = curve[i + 1]; + final double len = Helpers.linelen(x0, y0, x1, y1); polyLen += len; curLeafCtrlPolyLengths[(i >> 1) - 1] = len; x0 = x1; y0 = y1; } - final float lineLen = Helpers.linelen(curve[0], curve[1], x0, y0); + final double lineLen = Helpers.linelen(curve[0], curve[1], x0, y0); if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) { - return (polyLen + lineLen) / 2.0f; + return (polyLen + lineLen) / 2.0d; } - return -1.0f; + return -1.0d; } } @Override - public void curveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + public void curveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { final int outcode0 = this.cOutCode; @@ -969,18 +968,18 @@ public void curveTo(final float x1, final float y1, _curveTo(x1, y1, x2, y2, x3, y3); } - private void _curveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + private void _curveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; // monotonize curve: final CurveBasicMonotonizer monotonizer = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3); final int nSplits = monotonizer.nbSplits; - final float[] mid = monotonizer.middle; + final double[] mid = monotonizer.middle; // Implicitely rdrCtx.isFirstSegment = true @@ -997,11 +996,11 @@ private void _curveTo(final float x1, final float y1, rdrCtx.isFirstSegment = true; } - private void skipCurveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + private void skipCurveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; _curCurvepts[0] = cx0; _curCurvepts[1] = cy0; _curCurvepts[2] = x1; _curCurvepts[3] = y1; _curCurvepts[4] = x2; _curCurvepts[5] = y2; @@ -1014,8 +1013,8 @@ private void skipCurveTo(final float x1, final float y1, } @Override - public void quadTo(final float x1, final float y1, - final float x2, final float y2) + public void quadTo(final double x1, final double y1, + final double x2, final double y2) { final int outcode0 = this.cOutCode; @@ -1062,17 +1061,17 @@ public void quadTo(final float x1, final float y1, _quadTo(x1, y1, x2, y2); } - private void _quadTo(final float x1, final float y1, - final float x2, final float y2) + private void _quadTo(final double x1, final double y1, + final double x2, final double y2) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; // monotonize quad: final CurveBasicMonotonizer monotonizer = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2); final int nSplits = monotonizer.nbSplits; - final float[] mid = monotonizer.middle; + final double[] mid = monotonizer.middle; // Implicitely rdrCtx.isFirstSegment = true @@ -1089,10 +1088,10 @@ private void _quadTo(final float x1, final float y1, rdrCtx.isFirstSegment = true; } - private void skipQuadTo(final float x1, final float y1, - final float x2, final float y2) + private void skipQuadTo(final double x1, final double y1, + final double x2, final double y2) { - final float[] _curCurvepts = curCurvepts; + final double[] _curCurvepts = curCurvepts; _curCurvepts[0] = cx0; _curCurvepts[1] = cy0; _curCurvepts[2] = x1; _curCurvepts[3] = y1; _curCurvepts[4] = x2; _curCurvepts[5] = y2; diff --git a/src/main/java/sun/java2d/marlin/Helpers.java b/src/main/java/sun/java2d/marlin/Helpers.java index 63788ee..48b5ab0 100644 --- a/src/main/java/sun/java2d/marlin/Helpers.java +++ b/src/main/java/sun/java2d/marlin/Helpers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,32 +26,30 @@ package sun.java2d.marlin; import java.util.Arrays; -import net.jafama.FastMath; import sun.java2d.marlin.stats.Histogram; import sun.java2d.marlin.stats.StatLong; -import sun.awt.geom.PathConsumer2D; final class Helpers implements MarlinConst { - private static final float EPS = 1e-9f; + private static final double EPS = 1e-9d; private Helpers() { throw new Error("This is a non instantiable class"); } - static boolean within(final float x, final float y) { + static boolean within(final double x, final double y) { return within(x, y, EPS); } - static boolean within(final float x, final float y, final float err) { + static boolean within(final double x, final double y, final double err) { return withinD(y - x, err); } - static boolean withinD(final float d, final float err) { + static boolean withinD(final double d, final double err) { return (d <= err && d >= -err); } - static boolean withinD(final float dx, final float dy, final float err) + static boolean withinD(final double dx, final double dy, final double err) { assert err > 0 : ""; // compare taxicab distance. ERR will always be small, so using @@ -60,11 +58,11 @@ static boolean withinD(final float dx, final float dy, final float err) withinD(dy, err)); // this is just as good. } - static boolean isPointCurve(final float[] curve, final int type) { + static boolean isPointCurve(final double[] curve, final int type) { return isPointCurve(curve, type, EPS); } - static boolean isPointCurve(final float[] curve, final int type, final float err) { + static boolean isPointCurve(final double[] curve, final int type, final double err) { for (int i = 2; i < type; i++) { if (!within(curve[i], curve[i - 2], err)) { return false; @@ -73,57 +71,57 @@ static boolean isPointCurve(final float[] curve, final int type, final float err return true; } - static float evalCubic(final float a, final float b, - final float c, final float d, - final float t) + static double evalCubic(final double a, final double b, + final double c, final double d, + final double t) { return t * (t * (t * a + b) + c) + d; } - static float evalQuad(final float a, final float b, - final float c, final float t) + static double evalQuad(final double a, final double b, + final double c, final double t) { return t * (t * a + b) + c; } - static int quadraticRoots(final float a, final float b, final float c, - final float[] zeroes, final int off) + static int quadraticRoots(final double a, final double b, final double c, + final double[] zeroes, final int off) { int ret = off; - if (a != 0.0f) { - float d = b * b - 4.0f * a * c; - if (d > 0.0f) { - d = (float)Math.sqrt(d); + if (a != 0.0d) { + double d = b * b - 4.0d * a * c; + if (d > 0.0d) { + d = Math.sqrt(d); // For accuracy, calculate one root using: // (-b +/- d) / 2a // and the other using: // 2c / (-b +/- d) // Choose the sign of the +/- so that b+d gets larger in magnitude - if (b < 0.0f) { + if (b < 0.0d) { d = -d; } - final float q = (b + d) / -2.0f; + final double q = (b + d) / -2.0d; // We already tested a for being 0 above zeroes[ret++] = q / a; - if (q != 0.0f) { + if (q != 0.0d) { zeroes[ret++] = c / q; } - } else if (d == 0.0f) { - zeroes[ret++] = -b / (2.0f * a); + } else if (d == 0.0d) { + zeroes[ret++] = -b / (2.0d * a); } - } else if (b != 0.0f) { + } else if (b != 0.0d) { zeroes[ret++] = -c / b; } return ret - off; } // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B) - static int cubicRootsInAB(final float d0, float a0, float b0, float c0, - final float[] pts, final int off, - final float A, final float B) + static int cubicRootsInAB(final double d, double a, double b, double c, + final double[] pts, final int off, + final double A, final double B) { - if (d0 == 0.0f) { - final int num = quadraticRoots(a0, b0, c0, pts, off); + if (d == 0.0d) { + final int num = quadraticRoots(a, b, c, pts, off); return filterOutNotInAB(pts, off, num, A, B) - off; } // From Graphics Gems: @@ -133,11 +131,9 @@ static int cubicRootsInAB(final float d0, float a0, float b0, float c0, // our own customized version). // normal form: x^3 + ax^2 + bx + c = 0 - - // 2018.1: Need double precision if d is very small (flat curve) ! - final double a = ((double)a0) / d0; - final double b = ((double)b0) / d0; - final double c = ((double)c0) / d0; + a /= d; + b /= d; + c /= d; // substitute x = y - A/3 to eliminate quadratic term: // x^3 +Px + Q = 0 @@ -158,41 +154,41 @@ static int cubicRootsInAB(final float d0, float a0, float b0, float c0, int num; - if (DHelpers.within(D, 0.0d)) { - if (DHelpers.within(q, 0.0d)) { + if (within(D, 0.0d)) { + if (within(q, 0.0d)) { /* one triple solution */ - pts[off ] = (float) (- sub); + pts[off ] = (- sub); num = 1; } else { /* one single and one double solution */ - final double u = FastMath.cbrt(-q); - pts[off ] = (float) (2.0d * u - sub); - pts[off + 1] = (float) (- u - sub); + final double u = Math.cbrt(-q); + pts[off ] = (2.0d * u - sub); + pts[off + 1] = (- u - sub); num = 2; } } else if (D < 0.0d) { // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method - final double phi = (1.0d / 3.0d) * FastMath.acos(-q / Math.sqrt(-cb_p)); + final double phi = (1.0d / 3.0d) * Math.acos(-q / Math.sqrt(-cb_p)); final double t = 2.0d * Math.sqrt(-p); - pts[off ] = (float) ( t * FastMath.cos(phi) - sub); - pts[off + 1] = (float) (-t * FastMath.cos(phi + (Math.PI / 3.0d)) - sub); - pts[off + 2] = (float) (-t * FastMath.cos(phi - (Math.PI / 3.0d)) - sub); + pts[off ] = ( t * Math.cos(phi) - sub); + pts[off + 1] = (-t * Math.cos(phi + (Math.PI / 3.0d)) - sub); + pts[off + 2] = (-t * Math.cos(phi - (Math.PI / 3.0d)) - sub); num = 3; } else { final double sqrt_D = Math.sqrt(D); - final double u = FastMath.cbrt(sqrt_D - q); - final double v = - FastMath.cbrt(sqrt_D + q); + final double u = Math.cbrt(sqrt_D - q); + final double v = - Math.cbrt(sqrt_D + q); - pts[off ] = (float) (u + v - sub); + pts[off ] = (u + v - sub); num = 1; } return filterOutNotInAB(pts, off, num, A, B) - off; } // returns the index 1 past the last valid element remaining after filtering - static int filterOutNotInAB(final float[] nums, final int off, final int len, - final float a, final float b) + static int filterOutNotInAB(final double[] nums, final int off, final int len, + final double a, final double b) { int ret = off; for (int i = off, end = off + len; i < end; i++) { @@ -203,105 +199,104 @@ static int filterOutNotInAB(final float[] nums, final int off, final int len, return ret; } - static float fastLineLen(final float x0, final float y0, - final float x1, final float y1) + static double fastLineLen(final double x0, final double y0, + final double x1, final double y1) { - final float dx = x1 - x0; - final float dy = y1 - y0; + final double dx = x1 - x0; + final double dy = y1 - y0; // use manhattan norm: return Math.abs(dx) + Math.abs(dy); } - static float linelen(final float x0, final float y0, - final float x1, final float y1) + static double linelen(final double x0, final double y0, + final double x1, final double y1) { - final float dx = x1 - x0; - final float dy = y1 - y0; - return (float) Math.sqrt(dx * dx + dy * dy); + final double dx = x1 - x0; + final double dy = y1 - y0; + return Math.sqrt(dx * dx + dy * dy); } - static float fastQuadLen(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2) + static double fastQuadLen(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2) { - final float dx1 = x1 - x0; - final float dx2 = x2 - x1; - final float dy1 = y1 - y0; - final float dy2 = y2 - y1; + final double dx1 = x1 - x0; + final double dx2 = x2 - x1; + final double dy1 = y1 - y0; + final double dy2 = y2 - y1; // use manhattan norm: return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dy1) + Math.abs(dy2); } - static float quadlen(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2) + static double quadlen(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2) { return (linelen(x0, y0, x1, y1) + linelen(x1, y1, x2, y2) - + linelen(x0, y0, x2, y2)) / 2.0f; + + linelen(x0, y0, x2, y2)) / 2.0d; } - - static float fastCurvelen(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + static double fastCurvelen(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { - final float dx1 = x1 - x0; - final float dx2 = x2 - x1; - final float dx3 = x3 - x2; - final float dy1 = y1 - y0; - final float dy2 = y2 - y1; - final float dy3 = y3 - y2; + final double dx1 = x1 - x0; + final double dx2 = x2 - x1; + final double dx3 = x3 - x2; + final double dy1 = y1 - y0; + final double dy2 = y2 - y1; + final double dy3 = y3 - y2; // use manhattan norm: return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3) + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3); } - static float curvelen(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + static double curvelen(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { return (linelen(x0, y0, x1, y1) + linelen(x1, y1, x2, y2) + linelen(x2, y2, x3, y3) - + linelen(x0, y0, x3, y3)) / 2.0f; + + linelen(x0, y0, x3, y3)) / 2.0d; } // finds values of t where the curve in pts should be subdivided in order // to get good offset curves a distance of w away from the middle curve. // Stores the points in ts, and returns how many of them there were. - static int findSubdivPoints(final Curve c, final float[] pts, - final float[] ts, final int type, - final float w2) + static int findSubdivPoints(final Curve c, final double[] pts, + final double[] ts, final int type, + final double w2) { - final float x12 = pts[2] - pts[0]; - final float y12 = pts[3] - pts[1]; + final double x12 = pts[2] - pts[0]; + final double y12 = pts[3] - pts[1]; // if the curve is already parallel to either axis we gain nothing // from rotating it. - if ((y12 != 0.0f) && (x12 != 0.0f)) { + if ((y12 != 0.0d) && (x12 != 0.0d)) { // we rotate it so that the first vector in the control polygon is // parallel to the x-axis. This will ensure that rotated quarter // circles won't be subdivided. - final float hypot = (float)Math.sqrt(x12 * x12 + y12 * y12); - final float cos = x12 / hypot; - final float sin = y12 / hypot; - final float x1 = cos * pts[0] + sin * pts[1]; - final float y1 = cos * pts[1] - sin * pts[0]; - final float x2 = cos * pts[2] + sin * pts[3]; - final float y2 = cos * pts[3] - sin * pts[2]; - final float x3 = cos * pts[4] + sin * pts[5]; - final float y3 = cos * pts[5] - sin * pts[4]; + final double hypot = Math.sqrt(x12 * x12 + y12 * y12); + final double cos = x12 / hypot; + final double sin = y12 / hypot; + final double x1 = cos * pts[0] + sin * pts[1]; + final double y1 = cos * pts[1] - sin * pts[0]; + final double x2 = cos * pts[2] + sin * pts[3]; + final double y2 = cos * pts[3] - sin * pts[2]; + final double x3 = cos * pts[4] + sin * pts[5]; + final double y3 = cos * pts[5] - sin * pts[4]; switch(type) { case 8: - final float x4 = cos * pts[6] + sin * pts[7]; - final float y4 = cos * pts[7] - sin * pts[6]; + final double x4 = cos * pts[6] + sin * pts[7]; + final double y4 = cos * pts[7] - sin * pts[6]; c.set(x1, y1, x2, y2, x3, y3, x4, y4); break; case 6: @@ -327,9 +322,9 @@ static int findSubdivPoints(final Curve c, final float[] pts, // now we must subdivide at points where one of the offset curves will have // a cusp. This happens at ts where the radius of curvature is equal to w. - ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001f); + ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d); - ret = filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f); + ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d); isort(ts, ret); return ret; } @@ -337,10 +332,10 @@ static int findSubdivPoints(final Curve c, final float[] pts, // finds values of t where the curve in pts should be subdivided in order // to get intersections with the given clip rectangle. // Stores the points in ts, and returns how many of them there were. - static int findClipPoints(final Curve curve, final float[] pts, - final float[] ts, final int type, + static int findClipPoints(final Curve curve, final double[] pts, + final double[] ts, final int type, final int outCodeOR, - final float[] clipRect) + final double[] clipRect) { curve.set(pts, type); @@ -363,8 +358,8 @@ static int findClipPoints(final Curve curve, final float[] pts, return ret; } - static void subdivide(final float[] src, - final float[] left, final float[] right, + static void subdivide(final double[] src, + final double[] left, final double[] right, final int type) { switch(type) { @@ -379,9 +374,9 @@ static void subdivide(final float[] src, } } - static void isort(final float[] a, final int len) { + static void isort(final double[] a, final int len) { for (int i = 1, j; i < len; i++) { - final float ai = a[i]; + final double ai = a[i]; j = i - 1; for (; j >= 0 && a[j] > ai; j--) { a[j + 1] = a[j]; @@ -415,18 +410,18 @@ static void isort(final float[] a, final int len) { * half of the subdivided curve * @since 1.7 */ - static void subdivideCubic(final float[] src, - final float[] left, - final float[] right) + static void subdivideCubic(final double[] src, + final double[] left, + final double[] right) { - float x1 = src[0]; - float y1 = src[1]; - float cx1 = src[2]; - float cy1 = src[3]; - float cx2 = src[4]; - float cy2 = src[5]; - float x2 = src[6]; - float y2 = src[7]; + double x1 = src[0]; + double y1 = src[1]; + double cx1 = src[2]; + double cy1 = src[3]; + double cx2 = src[4]; + double cy2 = src[5]; + double x2 = src[6]; + double y2 = src[7]; left[0] = x1; left[1] = y1; @@ -434,20 +429,20 @@ static void subdivideCubic(final float[] src, right[6] = x2; right[7] = y2; - x1 = (x1 + cx1) / 2.0f; - y1 = (y1 + cy1) / 2.0f; - x2 = (x2 + cx2) / 2.0f; - y2 = (y2 + cy2) / 2.0f; + x1 = (x1 + cx1) / 2.0d; + y1 = (y1 + cy1) / 2.0d; + x2 = (x2 + cx2) / 2.0d; + y2 = (y2 + cy2) / 2.0d; - float cx = (cx1 + cx2) / 2.0f; - float cy = (cy1 + cy2) / 2.0f; + double cx = (cx1 + cx2) / 2.0d; + double cy = (cy1 + cy2) / 2.0d; - cx1 = (x1 + cx) / 2.0f; - cy1 = (y1 + cy) / 2.0f; - cx2 = (x2 + cx) / 2.0f; - cy2 = (y2 + cy) / 2.0f; - cx = (cx1 + cx2) / 2.0f; - cy = (cy1 + cy2) / 2.0f; + cx1 = (x1 + cx) / 2.0d; + cy1 = (y1 + cy) / 2.0d; + cx2 = (x2 + cx) / 2.0d; + cy2 = (y2 + cy) / 2.0d; + cx = (cx1 + cx2) / 2.0d; + cy = (cy1 + cy2) / 2.0d; left[2] = x1; left[3] = y1; @@ -464,18 +459,18 @@ static void subdivideCubic(final float[] src, right[5] = y2; } - static void subdivideCubicAt(final float t, - final float[] src, final int offS, - final float[] pts, final int offL, final int offR) + static void subdivideCubicAt(final double t, + final double[] src, final int offS, + final double[] pts, final int offL, final int offR) { - float x1 = src[offS ]; - float y1 = src[offS + 1]; - float cx1 = src[offS + 2]; - float cy1 = src[offS + 3]; - float cx2 = src[offS + 4]; - float cy2 = src[offS + 5]; - float x2 = src[offS + 6]; - float y2 = src[offS + 7]; + double x1 = src[offS ]; + double y1 = src[offS + 1]; + double cx1 = src[offS + 2]; + double cy1 = src[offS + 3]; + double cx2 = src[offS + 4]; + double cy2 = src[offS + 5]; + double x2 = src[offS + 6]; + double y2 = src[offS + 7]; pts[offL ] = x1; pts[offL + 1] = y1; @@ -488,8 +483,8 @@ static void subdivideCubicAt(final float t, x2 = cx2 + t * (x2 - cx2); y2 = cy2 + t * (y2 - cy2); - float cx = cx1 + t * (cx2 - cx1); - float cy = cy1 + t * (cy2 - cy1); + double cx = cx1 + t * (cx2 - cx1); + double cy = cy1 + t * (cy2 - cy1); cx1 = x1 + t * (cx - x1); cy1 = y1 + t * (cy - y1); @@ -513,16 +508,16 @@ static void subdivideCubicAt(final float t, pts[offR + 5] = y2; } - static void subdivideQuad(final float[] src, - final float[] left, - final float[] right) + static void subdivideQuad(final double[] src, + final double[] left, + final double[] right) { - float x1 = src[0]; - float y1 = src[1]; - float cx = src[2]; - float cy = src[3]; - float x2 = src[4]; - float y2 = src[5]; + double x1 = src[0]; + double y1 = src[1]; + double cx = src[2]; + double cy = src[3]; + double x2 = src[4]; + double y2 = src[5]; left[0] = x1; left[1] = y1; @@ -530,12 +525,12 @@ static void subdivideQuad(final float[] src, right[4] = x2; right[5] = y2; - x1 = (x1 + cx) / 2.0f; - y1 = (y1 + cy) / 2.0f; - x2 = (x2 + cx) / 2.0f; - y2 = (y2 + cy) / 2.0f; - cx = (x1 + x2) / 2.0f; - cy = (y1 + y2) / 2.0f; + x1 = (x1 + cx) / 2.0d; + y1 = (y1 + cy) / 2.0d; + x2 = (x2 + cx) / 2.0d; + y2 = (y2 + cy) / 2.0d; + cx = (x1 + x2) / 2.0d; + cy = (y1 + y2) / 2.0d; left[2] = x1; left[3] = y1; @@ -548,16 +543,16 @@ static void subdivideQuad(final float[] src, right[3] = y2; } - static void subdivideQuadAt(final float t, - final float[] src, final int offS, - final float[] pts, final int offL, final int offR) + static void subdivideQuadAt(final double t, + final double[] src, final int offS, + final double[] pts, final int offL, final int offR) { - float x1 = src[offS ]; - float y1 = src[offS + 1]; - float cx = src[offS + 2]; - float cy = src[offS + 3]; - float x2 = src[offS + 4]; - float y2 = src[offS + 5]; + double x1 = src[offS ]; + double y1 = src[offS + 1]; + double cx = src[offS + 2]; + double cy = src[offS + 3]; + double x2 = src[offS + 4]; + double y2 = src[offS + 5]; pts[offL ] = x1; pts[offL + 1] = y1; @@ -583,14 +578,14 @@ static void subdivideQuadAt(final float t, pts[offR + 3] = y2; } - static void subdivideLineAt(final float t, - final float[] src, final int offS, - final float[] pts, final int offL, final int offR) + static void subdivideLineAt(final double t, + final double[] src, final int offS, + final double[] pts, final int offL, final int offR) { - float x1 = src[offS ]; - float y1 = src[offS + 1]; - float x2 = src[offS + 2]; - float y2 = src[offS + 3]; + double x1 = src[offS ]; + double y1 = src[offS + 1]; + double x2 = src[offS + 2]; + double y2 = src[offS + 3]; pts[offL ] = x1; pts[offL + 1] = y1; @@ -608,9 +603,9 @@ static void subdivideLineAt(final float t, pts[offR + 1] = y1; } - static void subdivideAt(final float t, - final float[] src, final int offS, - final float[] pts, final int offL, final int type) + static void subdivideAt(final double t, + final double[] src, final int offS, + final double[] pts, final int offL, final int type) { // if instead of switch (perf + most probable cases first) if (type == 8) { @@ -624,8 +619,8 @@ static void subdivideAt(final float t, // From sun.java2d.loops.GeneralRenderer: - static int outcode(final float x, final float y, - final float[] clipRect) + static int outcode(final double x, final double y, + final double[] clipRect) { int code; if (y < clipRect[0]) { @@ -656,13 +651,13 @@ static final class PolyStack { // types capacity = edges count (4096) private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT; - float[] curves; + double[] curves; int end; byte[] curveTypes; int numCurves; // curves ref (dirty) - final ArrayCacheFloat.Reference curves_ref; + final ArrayCacheDouble.Reference curves_ref; // curveTypes ref (dirty) final ArrayCacheByte.Reference curveTypes_ref; @@ -687,7 +682,7 @@ static final class PolyStack { final StatLong stat_array_polystack_curves, final StatLong stat_array_polystack_curveTypes) { - curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K + curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K curves = curves_ref.initial; curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K @@ -752,14 +747,14 @@ private void ensureSpace(final int n) { } } - void pushCubic(float x0, float y0, - float x1, float y1, - float x2, float y2) + void pushCubic(double x0, double y0, + double x1, double y1, + double x2, double y2) { ensureSpace(6); curveTypes[numCurves++] = TYPE_CUBICTO; // we reverse the coordinate order to make popping easier - final float[] _curves = curves; + final double[] _curves = curves; int e = end; _curves[e++] = x2; _curves[e++] = y2; _curves[e++] = x1; _curves[e++] = y1; @@ -767,25 +762,25 @@ void pushCubic(float x0, float y0, end = e; } - void pushQuad(float x0, float y0, - float x1, float y1) + void pushQuad(double x0, double y0, + double x1, double y1) { ensureSpace(4); curveTypes[numCurves++] = TYPE_QUADTO; - final float[] _curves = curves; + final double[] _curves = curves; int e = end; _curves[e++] = x1; _curves[e++] = y1; _curves[e++] = x0; _curves[e++] = y0; end = e; } - void pushLine(float x, float y) { + void pushLine(double x, double y) { ensureSpace(2); curveTypes[numCurves++] = TYPE_LINETO; curves[end++] = x; curves[end++] = y; } - void pullAll(final PathConsumer2D io) { + void pullAll(final DPathConsumer2D io) { final int nc = numCurves; if (nc == 0) { return; @@ -800,7 +795,7 @@ void pullAll(final PathConsumer2D io) { } } final byte[] _curveTypes = curveTypes; - final float[] _curves = curves; + final double[] _curves = curves; int e = 0; for (int i = 0; i < nc; i++) { @@ -827,7 +822,7 @@ void pullAll(final PathConsumer2D io) { end = 0; } - void popAll(final PathConsumer2D io) { + void popAll(final DPathConsumer2D io) { int nc = numCurves; if (nc == 0) { return; @@ -842,7 +837,7 @@ void popAll(final PathConsumer2D io) { } } final byte[] _curveTypes = curveTypes; - final float[] _curves = curves; + final double[] _curves = curves; int e = end; while (nc != 0) { @@ -997,7 +992,7 @@ void push(final int v) { } } - void pullAll(final float[] points, final PathConsumer2D io, + void pullAll(final double[] points, final DPathConsumer2D io, final boolean moveFirst) { final int nc = end; diff --git a/src/main/java/sun/java2d/marlin/IRendererContext.java b/src/main/java/sun/java2d/marlin/IRendererContext.java deleted file mode 100644 index 7dd58dd..0000000 --- a/src/main/java/sun/java2d/marlin/IRendererContext.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -interface IRendererContext extends MarlinConst { - - public RendererStats stats(); - - public OffHeapArray newOffHeapArray(final long initialSize); - - public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize); - -} diff --git a/src/main/java/sun/java2d/marlin/MarlinCache.java b/src/main/java/sun/java2d/marlin/MarlinCache.java index 5b9ae76..e9ffbd0 100644 --- a/src/main/java/sun/java2d/marlin/MarlinCache.java +++ b/src/main/java/sun/java2d/marlin/MarlinCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -107,7 +107,7 @@ public final class MarlinCache implements MarlinConst { boolean useRLE = false; - MarlinCache(final IRendererContext rdrCtx) { + MarlinCache(final RendererContext rdrCtx) { this.rdrStats = rdrCtx.stats(); rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K diff --git a/src/main/java/sun/java2d/marlin/MarlinProperties.java b/src/main/java/sun/java2d/marlin/MarlinProperties.java index 4ffee64..65b3dfd 100644 --- a/src/main/java/sun/java2d/marlin/MarlinProperties.java +++ b/src/main/java/sun/java2d/marlin/MarlinProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -317,6 +317,7 @@ public static boolean isLogUnsafeMalloc() { } // quality settings + public static float getCurveLengthError() { return getFloat("sun.java2d.renderer.curve_len_err", 0.01f, 1e-6f, 1.0f); } @@ -343,7 +344,7 @@ static String getString(final String key, final String def) { } static boolean getBoolean(final String key, final String def) { - return Boolean.valueOf(AccessController.doPrivileged( + return Boolean.parseBoolean(AccessController.doPrivileged( new GetPropertyAction(key, def))); } diff --git a/src/main/java/sun/java2d/marlin/MarlinRenderer.java b/src/main/java/sun/java2d/marlin/MarlinRenderer.java deleted file mode 100644 index 8b4e751..0000000 --- a/src/main/java/sun/java2d/marlin/MarlinRenderer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -public interface MarlinRenderer extends MarlinConst { - -} diff --git a/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java b/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java deleted file mode 100644 index cea94e2..0000000 --- a/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.marlin; - -import java.awt.BasicStroke; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; -import java.awt.geom.PathIterator; -import java.security.AccessController; -import java.util.Arrays; -import static sun.java2d.marlin.MarlinUtils.logInfo; -import sun.awt.geom.PathConsumer2D; -import sun.java2d.ReentrantContextProvider; -import sun.java2d.ReentrantContextProviderCLQ; -import sun.java2d.ReentrantContextProviderTL; -import sun.java2d.pipe.AATileGenerator; -import sun.java2d.pipe.Region; -import sun.java2d.pipe.RenderingEngine; -import sun.security.action.GetPropertyAction; - -/** - * Marlin RendererEngine implementation (derived from Pisces) - */ -public final class MarlinRenderingEngine extends RenderingEngine - implements MarlinConst -{ - // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases - static final boolean DISABLE_2ND_STROKER_CLIPPING = true; - - static final boolean DO_TRACE_PATH = false; - - static final boolean DO_CLIP = MarlinProperties.isDoClip(); - static final boolean DO_CLIP_FILL = true; - static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag(); - - private static final float MIN_PEN_SIZE = 1.0f / MIN_SUBPIXELS; - - static final float UPPER_BND = Float.MAX_VALUE / 2.0f; - static final float LOWER_BND = -UPPER_BND; - - private enum NormMode { - ON_WITH_AA { - @Override - PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, - final PathIterator src) - { - // NormalizingPathIterator NearestPixelCenter: - return rdrCtx.nPCPathIterator.init(src); - } - }, - ON_NO_AA{ - @Override - PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, - final PathIterator src) - { - // NearestPixel NormalizingPathIterator: - return rdrCtx.nPQPathIterator.init(src); - } - }, - OFF{ - @Override - PathIterator getNormalizingPathIterator(final RendererContext rdrCtx, - final PathIterator src) - { - // return original path iterator if normalization is disabled: - return src; - } - }; - - abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx, - PathIterator src); - } - - /** - * Public constructor - */ - public MarlinRenderingEngine() { - super(); - logSettings(MarlinRenderingEngine.class.getName()); - } - - /** - * Create a widened path as specified by the parameters. - *

- * The specified {@code src} {@link Shape} is widened according - * to the specified attribute parameters as per the - * {@link BasicStroke} specification. - * - * @param src the source path to be widened - * @param width the width of the widened path as per {@code BasicStroke} - * @param caps the end cap decorations as per {@code BasicStroke} - * @param join the segment join decorations as per {@code BasicStroke} - * @param miterlimit the miter limit as per {@code BasicStroke} - * @param dashes the dash length array as per {@code BasicStroke} - * @param dashphase the initial dash phase as per {@code BasicStroke} - * @return the widened path stored in a new {@code Shape} object - * @since 1.7 - */ - @Override - public Shape createStrokedShape(Shape src, - float width, - int caps, - int join, - float miterlimit, - float[] dashes, - float dashphase) - { - final RendererContext rdrCtx = getRendererContext(); - try { - // initialize a large copyable Path2D to avoid a lot of array growing: - final Path2D.Float p2d = rdrCtx.getPath2D(); - - strokeTo(rdrCtx, - src, - null, - width, - NormMode.OFF, - caps, - join, - miterlimit, - dashes, - dashphase, - rdrCtx.transformerPC2D.wrapPath2D(p2d) - ); - - // Use Path2D copy constructor (trim) - return new Path2D.Float(p2d); - - } finally { - // recycle the RendererContext instance - returnRendererContext(rdrCtx); - } - } - - /** - * Sends the geometry for a widened path as specified by the parameters - * to the specified consumer. - *

- * The specified {@code src} {@link Shape} is widened according - * to the parameters specified by the {@link BasicStroke} object. - * Adjustments are made to the path as appropriate for the - * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the - * {@code normalize} boolean parameter is true. - * Adjustments are made to the path as appropriate for the - * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the - * {@code antialias} boolean parameter is true. - *

- * The geometry of the widened path is forwarded to the indicated - * {@link PathConsumer2D} object as it is calculated. - * - * @param src the source path to be widened - * @param bs the {@code BasicSroke} object specifying the - * decorations to be applied to the widened path - * @param normalize indicates whether stroke normalization should - * be applied - * @param antialias indicates whether or not adjustments appropriate - * to antialiased rendering should be applied - * @param consumer the {@code PathConsumer2D} instance to forward - * the widened geometry to - * @since 1.7 - */ - @Override - public void strokeTo(Shape src, - AffineTransform at, - BasicStroke bs, - boolean thin, - boolean normalize, - boolean antialias, - final PathConsumer2D consumer) - { - final NormMode norm = (normalize) ? - ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) - : NormMode.OFF; - - final RendererContext rdrCtx = getRendererContext(); - try { - strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer); - } finally { - // recycle the RendererContext instance - returnRendererContext(rdrCtx); - } - } - - void strokeTo(final RendererContext rdrCtx, - Shape src, - AffineTransform at, - BasicStroke bs, - boolean thin, - NormMode normalize, - boolean antialias, - PathConsumer2D pc2d) - { - float lw; - if (thin) { - if (antialias) { - lw = userSpaceLineWidth(at, MIN_PEN_SIZE); - } else { - lw = userSpaceLineWidth(at, 1.0f); - } - } else { - lw = bs.getLineWidth(); - } - strokeTo(rdrCtx, - src, - at, - lw, - normalize, - bs.getEndCap(), - bs.getLineJoin(), - bs.getMiterLimit(), - bs.getDashArray(), - bs.getDashPhase(), - pc2d); - } - - private float userSpaceLineWidth(AffineTransform at, float lw) { - - float widthScale; - - if (at == null) { - widthScale = 1.0f; - } else if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM | - AffineTransform.TYPE_GENERAL_SCALE)) != 0) { - // Determinant may be negative (flip), use its absolute value: - widthScale = (float)Math.sqrt(Math.abs(at.getDeterminant())); - } else { - // First calculate the "maximum scale" of this transform. - double A = at.getScaleX(); // m00 - double C = at.getShearX(); // m01 - double B = at.getShearY(); // m10 - double D = at.getScaleY(); // m11 - - /* - * Given a 2 x 2 affine matrix [ A B ] such that - * [ C D ] - * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to - * find the maximum magnitude (norm) of the vector v' - * with the constraint (x^2 + y^2 = 1). - * The equation to maximize is - * |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2) - * or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2). - * Since sqrt is monotonic we can maximize |v'|^2 - * instead and plug in the substitution y = sqrt(1 - x^2). - * Trigonometric equalities can then be used to get - * rid of most of the sqrt terms. - */ - - double EA = A*A + B*B; // x^2 coefficient - double EB = 2.0d * (A*C + B*D); // xy coefficient - double EC = C*C + D*D; // y^2 coefficient - - /* - * There is a lot of calculus omitted here. - * - * Conceptually, in the interests of understanding the - * terms that the calculus produced we can consider - * that EA and EC end up providing the lengths along - * the major axes and the hypot term ends up being an - * adjustment for the additional length along the off-axis - * angle of rotated or sheared ellipses as well as an - * adjustment for the fact that the equation below - * averages the two major axis lengths. (Notice that - * the hypot term contains a part which resolves to the - * difference of these two axis lengths in the absence - * of rotation.) - * - * In the calculus, the ratio of the EB and (EA-EC) terms - * ends up being the tangent of 2*theta where theta is - * the angle that the long axis of the ellipse makes - * with the horizontal axis. Thus, this equation is - * calculating the length of the hypotenuse of a triangle - * along that axis. - */ - - double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC)); - // sqrt omitted, compare to squared limits below. - double widthsquared = ((EA + EC + hypot) / 2.0d); - - widthScale = (float)Math.sqrt(widthsquared); - } - - return (lw / widthScale); - } - - void strokeTo(final RendererContext rdrCtx, - Shape src, - AffineTransform at, - float width, - NormMode norm, - int caps, - int join, - float miterlimit, - float[] dashes, - float dashphase, - PathConsumer2D pc2d) - { - // We use strokerat so that in Stroker and Dasher we can work only - // with the pre-transformation coordinates. This will repeat a lot of - // computations done in the path iterator, but the alternative is to - // work with transformed paths and compute untransformed coordinates - // as needed. This would be faster but I do not think the complexity - // of working with both untransformed and transformed coordinates in - // the same code is worth it. - // However, if a path's width is constant after a transformation, - // we can skip all this untransforming. - - // As pathTo() will check transformed coordinates for invalid values - // (NaN / Infinity) to ignore such points, it is necessary to apply the - // transformation before the path processing. - AffineTransform strokerat = null; - - int dashLen = -1; - boolean recycleDashes = false; - - if (at != null && !at.isIdentity()) { - final double a = at.getScaleX(); - final double b = at.getShearX(); - final double c = at.getShearY(); - final double d = at.getScaleY(); - final double det = a * d - c * b; - - if (Math.abs(det) <= (2.0f * Float.MIN_VALUE)) { - // this rendering engine takes one dimensional curves and turns - // them into 2D shapes by giving them width. - // However, if everything is to be passed through a singular - // transformation, these 2D shapes will be squashed down to 1D - // again so, nothing can be drawn. - - // Every path needs an initial moveTo and a pathDone. If these - // are not there this causes a SIGSEGV in libawt.so (at the time - // of writing of this comment (September 16, 2010)). Actually, - // I am not sure if the moveTo is necessary to avoid the SIGSEGV - // but the pathDone is definitely needed. - pc2d.moveTo(0.0f, 0.0f); - pc2d.pathDone(); - return; - } - - // If the transform is a constant multiple of an orthogonal transformation - // then every length is just multiplied by a constant, so we just - // need to transform input paths to stroker and tell stroker - // the scaled width. This condition is satisfied if - // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we - // leave a bit of room for error. - if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) { - final float scale = (float) Math.sqrt(a*a + c*c); - - if (dashes != null) { - recycleDashes = true; - dashLen = dashes.length; - dashes = rdrCtx.dasher.copyDashArray(dashes); - for (int i = 0; i < dashLen; i++) { - dashes[i] *= scale; - } - dashphase *= scale; - } - width *= scale; - - // by now strokerat == null. Input paths to - // stroker (and maybe dasher) will have the full transform at - // applied to them and nothing will happen to the output paths. - } else { - strokerat = at; - - // by now strokerat == at. Input paths to - // stroker (and maybe dasher) will have the full transform at - // applied to them, then they will be normalized, and then - // the inverse of *only the non translation part of at* will - // be applied to the normalized paths. This won't cause problems - // in stroker, because, suppose at = T*A, where T is just the - // translation part of at, and A is the rest. T*A has already - // been applied to Stroker/Dasher's input. Then Ainv will be - // applied. Ainv*T*A is not equal to T, but it is a translation, - // which means that none of stroker's assumptions about its - // input will be violated. After all this, A will be applied - // to stroker's output. - } - } else { - // either at is null or it's the identity. In either case - // we don't transform the path. - at = null; - } - - final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D; - - if (DO_TRACE_PATH) { - // trace Stroker: - pc2d = transformerPC2D.traceStroker(pc2d); - } - - if (USE_SIMPLIFIER) { - // Use simplifier after stroker before Renderer - // to remove collinear segments (notably due to cap square) - pc2d = rdrCtx.simplifier.init(pc2d); - } - - // deltaTransformConsumer may adjust the clip rectangle: - pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat); - - // stroker will adjust the clip rectangle (width / miter limit): - pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, - (dashes == null)); - - // Curve Monotizer: - rdrCtx.monotonizer.init(width); - - if (dashes != null) { - if (!recycleDashes) { - dashLen = dashes.length; - } - if (DO_TRACE_PATH) { - pc2d = transformerPC2D.traceDasher(pc2d); - } - pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase, - recycleDashes); - - if (DISABLE_2ND_STROKER_CLIPPING) { - // disable stoker clipping - rdrCtx.stroker.disableClipping(); - } - - } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) { - if (DO_TRACE_PATH) { - pc2d = transformerPC2D.traceClosedPathDetector(pc2d); - } - - // If no dash and clip is enabled: - // detect closedPaths (polygons) for caps - pc2d = transformerPC2D.detectClosedPath(pc2d); - } - pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat); - - if (DO_TRACE_PATH) { - // trace Input: - pc2d = transformerPC2D.traceInput(pc2d); - } - - final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx, - src.getPathIterator(at)); - - pathTo(rdrCtx, pi, pc2d); - - /* - * Pipeline seems to be: - * shape.getPathIterator(at) - * -> (NormalizingPathIterator) - * -> (inverseDeltaTransformConsumer) - * -> (Dasher) - * -> Stroker - * -> (deltaTransformConsumer) - * - * -> (CollinearSimplifier) to remove redundant segments - * - * -> pc2d = Renderer (bounding box) - */ - } - - private static boolean nearZero(final double num) { - return Math.abs(num) < 2.0d * Math.ulp(num); - } - - abstract static class NormalizingPathIterator implements PathIterator { - - private PathIterator src; - - // the adjustment applied to the current position. - private float curx_adjust, cury_adjust; - // the adjustment applied to the last moveTo position. - private float movx_adjust, movy_adjust; - - private final float[] tmp; - - NormalizingPathIterator(final float[] tmp) { - this.tmp = tmp; - } - - final NormalizingPathIterator init(final PathIterator src) { - this.src = src; - return this; // fluent API - } - - /** - * Disposes this path iterator: - * clean up before reusing this instance - */ - final void dispose() { - // free source PathIterator: - this.src = null; - } - - @Override - public final int currentSegment(final float[] coords) { - int lastCoord; - final int type = src.currentSegment(coords); - - switch(type) { - case PathIterator.SEG_MOVETO: - case PathIterator.SEG_LINETO: - lastCoord = 0; - break; - case PathIterator.SEG_QUADTO: - lastCoord = 2; - break; - case PathIterator.SEG_CUBICTO: - lastCoord = 4; - break; - case PathIterator.SEG_CLOSE: - // we don't want to deal with this case later. We just exit now - curx_adjust = movx_adjust; - cury_adjust = movy_adjust; - return type; - default: - throw new InternalError("Unrecognized curve type"); - } - - // normalize endpoint - float coord, x_adjust, y_adjust; - - coord = coords[lastCoord]; - x_adjust = normCoord(coord); // new coord - coords[lastCoord] = x_adjust; - x_adjust -= coord; - - coord = coords[lastCoord + 1]; - y_adjust = normCoord(coord); // new coord - coords[lastCoord + 1] = y_adjust; - y_adjust -= coord; - - // now that the end points are done, normalize the control points - switch(type) { - case PathIterator.SEG_MOVETO: - movx_adjust = x_adjust; - movy_adjust = y_adjust; - break; - case PathIterator.SEG_LINETO: - break; - case PathIterator.SEG_QUADTO: - coords[0] += (curx_adjust + x_adjust) / 2.0f; - coords[1] += (cury_adjust + y_adjust) / 2.0f; - break; - case PathIterator.SEG_CUBICTO: - coords[0] += curx_adjust; - coords[1] += cury_adjust; - coords[2] += x_adjust; - coords[3] += y_adjust; - break; - case PathIterator.SEG_CLOSE: - // handled earlier - default: - } - curx_adjust = x_adjust; - cury_adjust = y_adjust; - return type; - } - - abstract float normCoord(final float coord); - - @Override - public final int currentSegment(final double[] coords) { - final float[] _tmp = tmp; // dirty - int type = this.currentSegment(_tmp); - for (int i = 0; i < 6; i++) { - coords[i] = _tmp[i]; - } - return type; - } - - @Override - public final int getWindingRule() { - return src.getWindingRule(); - } - - @Override - public final boolean isDone() { - if (src.isDone()) { - // Dispose this instance: - dispose(); - return true; - } - return false; - } - - @Override - public final void next() { - src.next(); - } - - static final class NearestPixelCenter - extends NormalizingPathIterator - { - NearestPixelCenter(final float[] tmp) { - super(tmp); - } - - @Override - float normCoord(final float coord) { - // round to nearest pixel center - return FloatMath.floor_f(coord) + 0.5f; - } - } - - static final class NearestPixelQuarter - extends NormalizingPathIterator - { - NearestPixelQuarter(final float[] tmp) { - super(tmp); - } - - @Override - float normCoord(final float coord) { - // round to nearest (0.25, 0.25) pixel quarter - return FloatMath.floor_f(coord + 0.25f) + 0.25f; - } - } - } - - private static void pathTo(final RendererContext rdrCtx, final PathIterator pi, - PathConsumer2D pc2d) - { - if (USE_PATH_SIMPLIFIER) { - // Use path simplifier at the first step - // to remove useless points - pc2d = rdrCtx.pathSimplifier.init(pc2d); - } - - // mark context as DIRTY: - rdrCtx.dirty = true; - - pathToLoop(rdrCtx.float6, pi, pc2d); - - // mark context as CLEAN: - rdrCtx.dirty = false; - } - - private static void pathToLoop(final float[] coords, final PathIterator pi, - final PathConsumer2D pc2d) - { - // ported from DuctusRenderingEngine.feedConsumer() but simplified: - // - removed skip flag = !subpathStarted - // - removed pathClosed (ie subpathStarted not set to false) - boolean subpathStarted = false; - - for (; !pi.isDone(); pi.next()) { - switch (pi.currentSegment(coords)) { - case PathIterator.SEG_MOVETO: - /* Checking SEG_MOVETO coordinates if they are out of the - * [LOWER_BND, UPPER_BND] range. This check also handles NaN - * and Infinity values. Skipping next path segment in case of - * invalid data. - */ - if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && - coords[1] < UPPER_BND && coords[1] > LOWER_BND) - { - pc2d.moveTo(coords[0], coords[1]); - subpathStarted = true; - } - break; - case PathIterator.SEG_LINETO: - /* Checking SEG_LINETO coordinates if they are out of the - * [LOWER_BND, UPPER_BND] range. This check also handles NaN - * and Infinity values. Ignoring current path segment in case - * of invalid data. If segment is skipped its endpoint - * (if valid) is used to begin new subpath. - */ - if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && - coords[1] < UPPER_BND && coords[1] > LOWER_BND) - { - if (subpathStarted) { - pc2d.lineTo(coords[0], coords[1]); - } else { - pc2d.moveTo(coords[0], coords[1]); - subpathStarted = true; - } - } - break; - case PathIterator.SEG_QUADTO: - // Quadratic curves take two points - /* Checking SEG_QUADTO coordinates if they are out of the - * [LOWER_BND, UPPER_BND] range. This check also handles NaN - * and Infinity values. Ignoring current path segment in case - * of invalid endpoints's data. Equivalent to the SEG_LINETO - * if endpoint coordinates are valid but there are invalid data - * among other coordinates - */ - if (coords[2] < UPPER_BND && coords[2] > LOWER_BND && - coords[3] < UPPER_BND && coords[3] > LOWER_BND) - { - if (subpathStarted) { - if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && - coords[1] < UPPER_BND && coords[1] > LOWER_BND) - { - pc2d.quadTo(coords[0], coords[1], - coords[2], coords[3]); - } else { - pc2d.lineTo(coords[2], coords[3]); - } - } else { - pc2d.moveTo(coords[2], coords[3]); - subpathStarted = true; - } - } - break; - case PathIterator.SEG_CUBICTO: - // Cubic curves take three points - /* Checking SEG_CUBICTO coordinates if they are out of the - * [LOWER_BND, UPPER_BND] range. This check also handles NaN - * and Infinity values. Ignoring current path segment in case - * of invalid endpoints's data. Equivalent to the SEG_LINETO - * if endpoint coordinates are valid but there are invalid data - * among other coordinates - */ - if (coords[4] < UPPER_BND && coords[4] > LOWER_BND && - coords[5] < UPPER_BND && coords[5] > LOWER_BND) - { - if (subpathStarted) { - if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && - coords[1] < UPPER_BND && coords[1] > LOWER_BND && - coords[2] < UPPER_BND && coords[2] > LOWER_BND && - coords[3] < UPPER_BND && coords[3] > LOWER_BND) - { - pc2d.curveTo(coords[0], coords[1], - coords[2], coords[3], - coords[4], coords[5]); - } else { - pc2d.lineTo(coords[4], coords[5]); - } - } else { - pc2d.moveTo(coords[4], coords[5]); - subpathStarted = true; - } - } - break; - case PathIterator.SEG_CLOSE: - if (subpathStarted) { - pc2d.closePath(); - // do not set subpathStarted to false - // in case of missing moveTo() after close() - } - break; - default: - } - } - pc2d.pathDone(); - } - - /** - * Construct an antialiased tile generator for the given shape with - * the given rendering attributes and store the bounds of the tile - * iteration in the bbox parameter. - * The {@code at} parameter specifies a transform that should affect - * both the shape and the {@code BasicStroke} attributes. - * The {@code clip} parameter specifies the current clip in effect - * in device coordinates and can be used to prune the data for the - * operation, but the renderer is not required to perform any - * clipping. - * If the {@code BasicStroke} parameter is null then the shape - * should be filled as is, otherwise the attributes of the - * {@code BasicStroke} should be used to specify a draw operation. - * The {@code thin} parameter indicates whether or not the - * transformed {@code BasicStroke} represents coordinates smaller - * than the minimum resolution of the antialiasing rasterizer as - * specified by the {@code getMinimumAAPenWidth()} method. - *

- * Upon returning, this method will fill the {@code bbox} parameter - * with 4 values indicating the bounds of the iteration of the - * tile generator. - * The iteration order of the tiles will be as specified by the - * pseudo-code: - *

-     *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
-     *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
-     *         }
-     *     }
-     * 
- * If there is no output to be rendered, this method may return - * null. - * - * @param s the shape to be rendered (fill or draw) - * @param at the transform to be applied to the shape and the - * stroke attributes - * @param clip the current clip in effect in device coordinates - * @param bs if non-null, a {@code BasicStroke} whose attributes - * should be applied to this operation - * @param thin true if the transformed stroke attributes are smaller - * than the minimum dropout pen width - * @param normalize true if the {@code VALUE_STROKE_NORMALIZE} - * {@code RenderingHint} is in effect - * @param bbox returns the bounds of the iteration - * @return the {@code AATileGenerator} instance to be consulted - * for tile coverages, or null if there is no output to render - * @since 1.7 - */ - @Override - public AATileGenerator getAATileGenerator(Shape s, - AffineTransform at, - Region clip, - BasicStroke bs, - boolean thin, - boolean normalize, - int[] bbox) - { - MarlinTileGenerator ptg = null; - Renderer r = null; - - final RendererContext rdrCtx = getRendererContext(); - try { - if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) { - // Define the initial clip bounds: - final float[] clipRect = rdrCtx.clipRect; - - // Adjust the clipping rectangle with the renderer offsets - final float rdrOffX = Renderer.RDR_OFFSET_X; - final float rdrOffY = Renderer.RDR_OFFSET_Y; - - // add a small rounding error: - final float margin = 1e-3f; - - clipRect[0] = clip.getLoY() - - margin + rdrOffY; - clipRect[1] = clip.getLoY() + clip.getHeight() - + margin + rdrOffY; - clipRect[2] = clip.getLoX() - - margin + rdrOffX; - clipRect[3] = clip.getLoX() + clip.getWidth() - + margin + rdrOffX; - - if (MarlinConst.DO_LOG_CLIP) { - MarlinUtils.logInfo("clipRect (clip): " - + Arrays.toString(rdrCtx.clipRect)); - } - - // Enable clipping: - rdrCtx.doClip = true; - } - - // Test if at is identity: - final AffineTransform _at = (at != null && !at.isIdentity()) ? at - : null; - - final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; - - if (bs == null) { - // fill shape: - final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx, - s.getPathIterator(_at)); - - // note: Winding rule may be EvenOdd ONLY for fill operations ! - r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - pi.getWindingRule()); - - PathConsumer2D pc2d = r; - - if (DO_CLIP_FILL && rdrCtx.doClip) { - if (DO_TRACE_PATH) { - // trace Filler: - pc2d = rdrCtx.transformerPC2D.traceFiller(pc2d); - } - pc2d = rdrCtx.transformerPC2D.pathClipper(pc2d); - } - - if (DO_TRACE_PATH) { - // trace Input: - pc2d = rdrCtx.transformerPC2D.traceInput(pc2d); - } - pathTo(rdrCtx, pi, pc2d); - - } else { - // draw shape with given stroke: - r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - WIND_NON_ZERO); - - strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r); - } - if (r.endRendering()) { - ptg = rdrCtx.ptg.init(); - ptg.getBbox(bbox); - // note: do not returnRendererContext(rdrCtx) - // as it will be called later by MarlinTileGenerator.dispose() - r = null; - } - } finally { - if (r != null) { - // dispose renderer and recycle the RendererContext instance: - r.dispose(); - } - } - - // Return null to cancel AA tile generation (nothing to render) - return ptg; - } - - @Override - public AATileGenerator getAATileGenerator(double x, double y, - double dx1, double dy1, - double dx2, double dy2, - double lw1, double lw2, - Region clip, - int[] bbox) - { - // REMIND: Deal with large coordinates! - double ldx1, ldy1, ldx2, ldy2; - boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d); - - if (innerpgram) { - ldx1 = dx1 * lw1; - ldy1 = dy1 * lw1; - ldx2 = dx2 * lw2; - ldy2 = dy2 * lw2; - x -= (ldx1 + ldx2) / 2.0d; - y -= (ldy1 + ldy2) / 2.0d; - dx1 += ldx1; - dy1 += ldy1; - dx2 += ldx2; - dy2 += ldy2; - if (lw1 > 1.0d && lw2 > 1.0d) { - // Inner parallelogram was entirely consumed by stroke... - innerpgram = false; - } - } else { - ldx1 = ldy1 = ldx2 = ldy2 = 0.0d; - } - - MarlinTileGenerator ptg = null; - Renderer r = null; - - final RendererContext rdrCtx = getRendererContext(); - try { - r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - WIND_EVEN_ODD); - - r.moveTo((float) x, (float) y); - r.lineTo((float) (x+dx1), (float) (y+dy1)); - r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); - r.lineTo((float) (x+dx2), (float) (y+dy2)); - r.closePath(); - - if (innerpgram) { - x += ldx1 + ldx2; - y += ldy1 + ldy2; - dx1 -= 2.0d * ldx1; - dy1 -= 2.0d * ldy1; - dx2 -= 2.0d * ldx2; - dy2 -= 2.0d * ldy2; - r.moveTo((float) x, (float) y); - r.lineTo((float) (x+dx1), (float) (y+dy1)); - r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); - r.lineTo((float) (x+dx2), (float) (y+dy2)); - r.closePath(); - } - r.pathDone(); - - if (r.endRendering()) { - ptg = rdrCtx.ptg.init(); - ptg.getBbox(bbox); - // note: do not returnRendererContext(rdrCtx) - // as it will be called later by MarlinTileGenerator.dispose() - r = null; - } - } finally { - if (r != null) { - // dispose renderer and recycle the RendererContext instance: - r.dispose(); - } - } - - // Return null to cancel AA tile generation (nothing to render) - return ptg; - } - - /** - * Returns the minimum pen width that the antialiasing rasterizer - * can represent without dropouts occuring. - * @since 1.7 - */ - @Override - public float getMinimumAAPenSize() { - return MIN_PEN_SIZE; - } - - static { - if (PathIterator.WIND_NON_ZERO != WIND_NON_ZERO || - PathIterator.WIND_EVEN_ODD != WIND_EVEN_ODD || - BasicStroke.JOIN_MITER != JOIN_MITER || - BasicStroke.JOIN_ROUND != JOIN_ROUND || - BasicStroke.JOIN_BEVEL != JOIN_BEVEL || - BasicStroke.CAP_BUTT != CAP_BUTT || - BasicStroke.CAP_ROUND != CAP_ROUND || - BasicStroke.CAP_SQUARE != CAP_SQUARE) - { - throw new InternalError("mismatched renderer constants"); - } - } - - // --- RendererContext handling --- - // use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext - private static final boolean USE_THREAD_LOCAL; - - // reference type stored in either TL or CLQ - static final int REF_TYPE; - - // Per-thread RendererContext - private static final ReentrantContextProvider RDR_CTX_PROVIDER; - - // Static initializer to use TL or CLQ mode - static { - USE_THREAD_LOCAL = MarlinProperties.isUseThreadLocal(); - - // Soft reference by default: - final String refType = AccessController.doPrivileged( - new GetPropertyAction("sun.java2d.renderer.useRef", - "soft")); - switch (refType) { - default: - case "soft": - REF_TYPE = ReentrantContextProvider.REF_SOFT; - break; - case "weak": - REF_TYPE = ReentrantContextProvider.REF_WEAK; - break; - case "hard": - REF_TYPE = ReentrantContextProvider.REF_HARD; - break; - } - - if (USE_THREAD_LOCAL) { - RDR_CTX_PROVIDER = new ReentrantContextProviderTL(REF_TYPE) - { - @Override - protected RendererContext newContext() { - return RendererContext.createContext(); - } - }; - } else { - RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ(REF_TYPE) - { - @Override - protected RendererContext newContext() { - return RendererContext.createContext(); - } - }; - } - } - - private static boolean SETTINGS_LOGGED = !ENABLE_LOGS; - - private static void logSettings(final String reClass) { - // log information at startup - if (SETTINGS_LOGGED) { - return; - } - SETTINGS_LOGGED = true; - - String refType; - switch (REF_TYPE) { - default: - case ReentrantContextProvider.REF_HARD: - refType = "hard"; - break; - case ReentrantContextProvider.REF_SOFT: - refType = "soft"; - break; - case ReentrantContextProvider.REF_WEAK: - refType = "weak"; - break; - } - - logInfo("==========================================================" - + "====================="); - - logInfo("Marlin software rasterizer = ENABLED"); - logInfo("Version = [" - + Version.getVersion() + "]"); - logInfo("sun.java2d.renderer = " - + reClass); - logInfo("sun.java2d.renderer.useThreadLocal = " - + USE_THREAD_LOCAL); - logInfo("sun.java2d.renderer.useRef = " - + refType); - - logInfo("sun.java2d.renderer.edges = " - + MarlinConst.INITIAL_EDGES_COUNT); - logInfo("sun.java2d.renderer.pixelWidth = " - + MarlinConst.INITIAL_PIXEL_WIDTH); - logInfo("sun.java2d.renderer.pixelHeight = " - + MarlinConst.INITIAL_PIXEL_HEIGHT); - - logInfo("sun.java2d.renderer.profile = " - + (MarlinProperties.isProfileQuality() ? - "quality" : "speed")); - - logInfo("sun.java2d.renderer.subPixel_log2_X = " - + MarlinConst.SUBPIXEL_LG_POSITIONS_X); - logInfo("sun.java2d.renderer.subPixel_log2_Y = " - + MarlinConst.SUBPIXEL_LG_POSITIONS_Y); - - logInfo("sun.java2d.renderer.tileSize_log2 = " - + MarlinConst.TILE_H_LG); - logInfo("sun.java2d.renderer.tileWidth_log2 = " - + MarlinConst.TILE_W_LG); - logInfo("sun.java2d.renderer.blockSize_log2 = " - + MarlinConst.BLOCK_SIZE_LG); - - // RLE / blockFlags settings - - logInfo("sun.java2d.renderer.forceRLE = " - + MarlinProperties.isForceRLE()); - logInfo("sun.java2d.renderer.forceNoRLE = " - + MarlinProperties.isForceNoRLE()); - logInfo("sun.java2d.renderer.useTileFlags = " - + MarlinProperties.isUseTileFlags()); - logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = " - + MarlinProperties.isUseTileFlagsWithHeuristics()); - logInfo("sun.java2d.renderer.rleMinWidth = " - + MarlinCache.RLE_MIN_WIDTH); - - // optimisation parameters - logInfo("sun.java2d.renderer.useSimplifier = " - + MarlinConst.USE_SIMPLIFIER); - logInfo("sun.java2d.renderer.usePathSimplifier= " - + MarlinConst.USE_PATH_SIMPLIFIER); - logInfo("sun.java2d.renderer.pathSimplifier.pixTol = " - + MarlinProperties.getPathSimplifierPixelTolerance()); - - logInfo("sun.java2d.renderer.clip = " - + MarlinProperties.isDoClip()); - logInfo("sun.java2d.renderer.clip.runtime.enable = " - + MarlinProperties.isDoClipRuntimeFlag()); - - logInfo("sun.java2d.renderer.clip.subdivider = " - + MarlinProperties.isDoClipSubdivider()); - logInfo("sun.java2d.renderer.clip.subdivider.minLength = " - + MarlinProperties.getSubdividerMinLength()); - - // debugging parameters - logInfo("sun.java2d.renderer.doStats = " - + MarlinConst.DO_STATS); - logInfo("sun.java2d.renderer.doMonitors = " - + MarlinConst.DO_MONITORS); - logInfo("sun.java2d.renderer.doChecks = " - + MarlinConst.DO_CHECKS); - - // logging parameters - logInfo("sun.java2d.renderer.useLogger = " - + MarlinConst.USE_LOGGER); - logInfo("sun.java2d.renderer.logCreateContext = " - + MarlinConst.LOG_CREATE_CONTEXT); - logInfo("sun.java2d.renderer.logUnsafeMalloc = " - + MarlinConst.LOG_UNSAFE_MALLOC); - - // quality settings - logInfo("sun.java2d.renderer.curve_len_err = " - + MarlinProperties.getCurveLengthError()); - logInfo("sun.java2d.renderer.cubic_dec_d2 = " - + MarlinProperties.getCubicDecD2()); - logInfo("sun.java2d.renderer.cubic_inc_d1 = " - + MarlinProperties.getCubicIncD1()); - logInfo("sun.java2d.renderer.quad_dec_d2 = " - + MarlinProperties.getQuadDecD2()); - - logInfo("Renderer settings:"); - logInfo("SORT = " + MergeSort.SORT_TYPE); - logInfo("CUB_DEC_BND = " + Renderer.CUB_DEC_BND); - logInfo("CUB_INC_BND = " + Renderer.CUB_INC_BND); - logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND); - - logInfo("INITIAL_EDGES_CAPACITY = " - + MarlinConst.INITIAL_EDGES_CAPACITY); - logInfo("INITIAL_CROSSING_COUNT = " - + Renderer.INITIAL_CROSSING_COUNT); - - logInfo("==========================================================" - + "====================="); - } - - /** - * Get the RendererContext instance dedicated to the current thread - * @return RendererContext instance - */ - @SuppressWarnings({"unchecked"}) - static RendererContext getRendererContext() { - final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire(); - if (DO_MONITORS) { - rdrCtx.stats.mon_pre_getAATileGenerator.start(); - } - return rdrCtx; - } - - /** - * Reset and return the given RendererContext instance for reuse - * @param rdrCtx RendererContext instance - */ - static void returnRendererContext(final RendererContext rdrCtx) { - rdrCtx.dispose(); - - if (DO_MONITORS) { - rdrCtx.stats.mon_pre_getAATileGenerator.stop(); - } - RDR_CTX_PROVIDER.release(rdrCtx); - } -} diff --git a/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java b/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java index 87b52c3..7721d78 100644 --- a/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java +++ b/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,25 +52,18 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst { } } - private final Renderer rdrF; - private final DRenderer rdrD; + private final Renderer renderer; private final MarlinCache cache; private int x, y; // per-thread renderer stats final RendererStats rdrStats; - MarlinTileGenerator(final RendererStats stats, final MarlinRenderer r, + MarlinTileGenerator(final RendererStats stats, final Renderer r, final MarlinCache cache) { this.rdrStats = stats; - if (r instanceof Renderer) { - this.rdrF = (Renderer)r; - this.rdrD = null; - } else { - this.rdrF = null; - this.rdrD = (DRenderer)r; - } + this.renderer = r; this.cache = cache; } @@ -94,12 +87,7 @@ public void dispose() { // dispose cache: cache.dispose(); // dispose renderer and recycle the RendererContext instance: - // bimorphic call optimization: - if (rdrF != null) { - rdrF.dispose(); - } else if (rdrD != null) { - rdrD.dispose(); - } + renderer.dispose(); } void getBbox(int[] bbox) { @@ -185,12 +173,7 @@ public void nextTile() { if (y < cache.bboxY1) { // compute for the tile line // [ y; max(y + TILE_SIZE, bboxY1) ] - // bimorphic call optimization: - if (rdrF != null) { - rdrF.endRendering(y); - } else if (rdrD != null) { - rdrD.endRendering(y); - } + renderer.endRendering(y); } } } diff --git a/src/main/java/sun/java2d/marlin/MergeSort.java b/src/main/java/sun/java2d/marlin/MergeSort.java index 969f486..0a05b60 100644 --- a/src/main/java/sun/java2d/marlin/MergeSort.java +++ b/src/main/java/sun/java2d/marlin/MergeSort.java @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package sun.java2d.marlin; import java.util.Arrays; diff --git a/src/main/java/sun/java2d/marlin/PathSimplifier.java b/src/main/java/sun/java2d/marlin/PathSimplifier.java index 203cbfd..82fcda3 100644 --- a/src/main/java/sun/java2d/marlin/PathSimplifier.java +++ b/src/main/java/sun/java2d/marlin/PathSimplifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,28 +24,26 @@ */ package sun.java2d.marlin; -import sun.awt.geom.PathConsumer2D; - -final class PathSimplifier implements PathConsumer2D { +final class PathSimplifier implements DPathConsumer2D { // distance threshold in pixels (device) - private static final float PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance(); + private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance(); // squared tolerance in pixels - private static final float SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD; + private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD; // members: - private PathConsumer2D delegate; + private DPathConsumer2D delegate; // current reference point - private float cx, cy; + private double cx, cy; // flag indicating if the given point was skipped private boolean skipped; // last skipped point - private float sx, sy; + private double sx, sy; PathSimplifier() { } - PathSimplifier init(final PathConsumer2D delegate) { + PathSimplifier init(final DPathConsumer2D delegate) { if (this.delegate != delegate) { this.delegate = delegate; } @@ -72,7 +70,7 @@ public void closePath() { } @Override - public void moveTo(final float xe, final float ye) { + public void moveTo(final double xe, final double ye) { finishPath(); delegate.moveTo(xe, ye); cx = xe; @@ -80,10 +78,10 @@ public void moveTo(final float xe, final float ye) { } @Override - public void lineTo(final float xe, final float ye) { + public void lineTo(final double xe, final double ye) { // Test if segment is too small: - float dx = (xe - cx); - float dy = (ye - cy); + double dx = (xe - cx); + double dy = (ye - cy); if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { skipped = true; @@ -94,7 +92,7 @@ public void lineTo(final float xe, final float ye) { _lineTo(xe, ye); } - private void _lineTo(final float xe, final float ye) { + private void _lineTo(final double xe, final double ye) { delegate.lineTo(xe, ye); cx = xe; cy = ye; @@ -102,12 +100,12 @@ private void _lineTo(final float xe, final float ye) { } @Override - public void quadTo(final float x1, final float y1, - final float xe, final float ye) + public void quadTo(final double x1, final double y1, + final double xe, final double ye) { // Test if curve is too small: - float dx = (xe - cx); - float dy = (ye - cy); + double dx = (xe - cx); + double dy = (ye - cy); if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { // check control points P1: @@ -128,13 +126,13 @@ public void quadTo(final float x1, final float y1, } @Override - public void curveTo(final float x1, final float y1, - final float x2, final float y2, - final float xe, final float ye) + public void curveTo(final double x1, final double y1, + final double x2, final double y2, + final double xe, final double ye) { // Test if curve is too small: - float dx = (xe - cx); - float dy = (ye - cy); + double dx = (xe - cx); + double dy = (ye - cy); if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) { // check control points P1: diff --git a/src/main/java/sun/java2d/marlin/Renderer.java b/src/main/java/sun/java2d/marlin/Renderer.java index 410e3ca..02522fb 100644 --- a/src/main/java/sun/java2d/marlin/Renderer.java +++ b/src/main/java/sun/java2d/marlin/Renderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,10 @@ package sun.java2d.marlin; -import sun.awt.geom.PathConsumer2D; import static sun.java2d.marlin.OffHeapArray.SIZE_INT; import sun.misc.Unsafe; -final class Renderer implements PathConsumer2D, MarlinRenderer { +final class Renderer implements DPathConsumer2D, MarlinConst { static final boolean DISABLE_RENDER = false; @@ -41,14 +40,14 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { private static final double POWER_2_TO_32 = 0x1.0p32d; - // use float to make tosubpix methods faster (no int to float conversion) - static final float SUBPIXEL_SCALE_X = (float) SUBPIXEL_POSITIONS_X; - static final float SUBPIXEL_SCALE_Y = (float) SUBPIXEL_POSITIONS_Y; + // use double to make tosubpix methods faster (no int to double conversion) + static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X; + static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y; static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1; static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1; - static final float RDR_OFFSET_X = 0.5f / SUBPIXEL_SCALE_X; - static final float RDR_OFFSET_Y = 0.5f / SUBPIXEL_SCALE_Y; + static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X; + static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y; // number of subpixels corresponding to a tile line private static final int SUBPIXEL_TILE @@ -76,13 +75,13 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { // curve break into lines // cubic error in subpixels to decrement step - private static final float CUB_DEC_ERR_SUBPIX - = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 1.0 / 8th pixel + private static final double CUB_DEC_ERR_SUBPIX + = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel // cubic error in subpixels to increment step - private static final float CUB_INC_ERR_SUBPIX - = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.4 / 8th pixel + private static final double CUB_INC_ERR_SUBPIX + = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel // scale factor for Y-axis contribution to quad / cubic errors: - public static final float SCALE_DY = ((float) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y; + public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y; // TestNonAARasterization (JDK-8170879): cubics // bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07) @@ -90,11 +89,11 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max = 9), 4042 warnings (avg = 0,06) // cubic bind length to decrement step - public static final float CUB_DEC_BND - = 8.0f * CUB_DEC_ERR_SUBPIX; + public static final double CUB_DEC_BND + = 8.0d * CUB_DEC_ERR_SUBPIX; // cubic bind length to increment step - public static final float CUB_INC_BND - = 8.0f * CUB_INC_ERR_SUBPIX; + public static final double CUB_INC_BND + = 8.0d * CUB_INC_ERR_SUBPIX; // cubic countlg public static final int CUB_COUNT_LG = 2; @@ -105,16 +104,16 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { // cubic count^3 = 8^countlg private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG); // cubic dt = 1 / count - private static final float CUB_INV_COUNT = 1.0f / CUB_COUNT; + private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT; // cubic dt^2 = 1 / count^2 = 1 / 4^countlg - private static final float CUB_INV_COUNT_2 = 1.0f / CUB_COUNT_2; + private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2; // cubic dt^3 = 1 / count^3 = 1 / 8^countlg - private static final float CUB_INV_COUNT_3 = 1.0f / CUB_COUNT_3; + private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3; // quad break into lines // quadratic error in subpixels - private static final float QUAD_DEC_ERR_SUBPIX - = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.5 / 8th pixel + private static final double QUAD_DEC_ERR_SUBPIX + = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel // TestNonAARasterization (JDK-8170879): quads // bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10) @@ -122,8 +121,8 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { // 0.50px = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10) // quadratic bind length to decrement step - public static final float QUAD_DEC_BND - = 8.0f * QUAD_DEC_ERR_SUBPIX; + public static final double QUAD_DEC_BND + = 8.0d * QUAD_DEC_ERR_SUBPIX; ////////////////////////////////////////////////////////////////////////////// // SCAN LINE @@ -159,8 +158,8 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { ////////////////////////////////////////////////////////////////////////////// private int edgeMinY = Integer.MAX_VALUE; private int edgeMaxY = Integer.MIN_VALUE; - private float edgeMinX = Float.POSITIVE_INFINITY; - private float edgeMaxX = Float.NEGATIVE_INFINITY; + private double edgeMinX = Double.POSITIVE_INFINITY; + private double edgeMaxX = Double.NEGATIVE_INFINITY; // edges [ints] stored in off-heap memory private final OffHeapArray edges; @@ -179,20 +178,20 @@ final class Renderer implements PathConsumer2D, MarlinRenderer { // Flattens using adaptive forward differencing. This only carries out // one iteration of the AFD loop. All it does is update AFD variables (i.e. // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). - private void quadBreakIntoLinesAndAdd(float x0, float y0, + private void quadBreakIntoLinesAndAdd(double x0, double y0, final Curve c, - final float x2, final float y2) + final double x2, final double y2) { int count = 1; // dt = 1 / count // maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1) - float maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY; + double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY; - final float _DEC_BND = QUAD_DEC_BND; + final double _DEC_BND = QUAD_DEC_BND; while (maxDD >= _DEC_BND) { // divide step by half: - maxDD /= 4.0f; // error divided by 2^2 = 4 + maxDD /= 4.0d; // error divided by 2^2 = 4 count <<= 1; if (DO_STATS) { @@ -203,16 +202,16 @@ private void quadBreakIntoLinesAndAdd(float x0, float y0, final int nL = count; // line count if (count > 1) { - final float icount = 1.0f / count; // dt - final float icount2 = icount * icount; // dt^2 + final double icount = 1.0d / count; // dt + final double icount2 = icount * icount; // dt^2 - final float ddx = c.dbx * icount2; - final float ddy = c.dby * icount2; - float dx = c.bx * icount2 + c.cx * icount; - float dy = c.by * icount2 + c.cy * icount; + final double ddx = c.dbx * icount2; + final double ddy = c.dby * icount2; + double dx = c.bx * icount2 + c.cx * icount; + double dy = c.by * icount2 + c.cy * icount; // we use x0, y0 to walk the line - for (float x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) { + for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) { x1 += dx; y1 += dy; @@ -233,20 +232,20 @@ private void quadBreakIntoLinesAndAdd(float x0, float y0, // numerical errors, and our callers already have the exact values. // Another alternative would be to pass all the control points, and call // c.set here, but then too many numbers are passed around. - private void curveBreakIntoLinesAndAdd(float x0, float y0, + private void curveBreakIntoLinesAndAdd(double x0, double y0, final Curve c, - final float x3, final float y3) + final double x3, final double y3) { - int count = CUB_COUNT; - final float icount = CUB_INV_COUNT; // dt - final float icount2 = CUB_INV_COUNT_2; // dt^2 - final float icount3 = CUB_INV_COUNT_3; // dt^3 + int count = CUB_COUNT; + final double icount = CUB_INV_COUNT; // dt + final double icount2 = CUB_INV_COUNT_2; // dt^2 + final double icount3 = CUB_INV_COUNT_3; // dt^3 // the dx and dy refer to forward differencing variables, not the last // coefficients of the "points" polynomial - float dddx, dddy, ddx, ddy, dx, dy; - dddx = 2.0f * c.dax * icount3; - dddy = 2.0f * c.day * icount3; + double dddx, dddy, ddx, ddy, dx, dy; + dddx = 2.0d * c.dax * icount3; + dddy = 2.0d * c.day * icount3; ddx = dddx + c.dbx * icount2; ddy = dddy + c.dby * icount2; dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount; @@ -254,24 +253,24 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0, int nL = 0; // line count - final float _DEC_BND = CUB_DEC_BND; - final float _INC_BND = CUB_INC_BND; - final float _SCALE_DY = SCALE_DY; + final double _DEC_BND = CUB_DEC_BND; + final double _INC_BND = CUB_INC_BND; + final double _SCALE_DY = SCALE_DY; // we use x0, y0 to walk the line - for (float x1 = x0, y1 = y0; count > 0; ) { + for (double x1 = x0, y1 = y0; count > 0; ) { // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges - // float step: + // double step: // can only do this on even "count" values, because we must divide count by 2 while ((count % 2 == 0) && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) { - dx = 2.0f * dx + ddx; - dy = 2.0f * dy + ddy; - ddx = 4.0f * (ddx + dddx); - ddy = 4.0f * (ddy + dddy); - dddx *= 8.0f; - dddy *= 8.0f; + dx = 2.0d * dx + ddx; + dy = 2.0d * dy + ddy; + ddx = 4.0d * (ddx + dddx); + ddy = 4.0d * (ddy + dddy); + dddx *= 8.0d; + dddy *= 8.0d; count >>= 1; if (DO_STATS) { @@ -281,12 +280,12 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0, // divide step by half: while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) { - dddx /= 8.0f; - dddy /= 8.0f; - ddx = ddx / 4.0f - dddx; - ddy = ddy / 4.0f - dddy; - dx = (dx - ddx) / 2.0f; - dy = (dy - ddy) / 2.0f; + dddx /= 8.0d; + dddy /= 8.0d; + ddx = ddx / 4.0d - dddx; + ddy = ddy / 4.0d - dddy; + dx = (dx - ddx) / 2.0d; + dy = (dy - ddy) / 2.0d; count <<= 1; if (DO_STATS) { @@ -315,7 +314,7 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0, } } - private void addLine(float x1, float y1, float x2, float y2) { + private void addLine(double x1, double y1, double x2, double y2) { if (DO_MONITORS) { rdrCtx.stats.mon_rdr_addLine.start(); } @@ -325,7 +324,7 @@ private void addLine(float x1, float y1, float x2, float y2) { int or = 1; // orientation of the line. 1 if y increases, 0 otherwise. if (y2 < y1) { or = 0; - float tmp = y2; + double tmp = y2; y2 = y1; y1 = tmp; tmp = x2; @@ -333,7 +332,7 @@ private void addLine(float x1, float y1, float x2, float y2) { x1 = tmp; } - // convert subpixel coordinates [float] into pixel positions [int] + // convert subpixel coordinates [double] into pixel positions [int] // The index of the pixel that holds the next HPC is at ceil(trueY - 0.5) // Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply @@ -367,10 +366,7 @@ private void addLine(float x1, float y1, float x2, float y2) { edgeMaxY = lastCrossing; } - // Use double-precision for improved accuracy: - final double x1d = x1; - final double y1d = y1; - final double slope = (x1d - x2) / (y1d - y2); + final double slope = (x1 - x2) / (y1 - y2); if (slope >= 0.0d) { // <==> x1 < x2 if (x1 < edgeMinX) { @@ -437,7 +433,7 @@ private void addLine(float x1, float y1, float x2, float y2) { // = fixed_floor(x1_fixed + 2^31 - 1) // = fixed_floor(x1_fixed + 0x7FFFFFFF) // and error = fixed_fract(x1_fixed + 0x7FFFFFFF) - final double x1_intercept = x1d + (firstCrossing - y1d) * slope; + final double x1_intercept = x1 + (firstCrossing - y1) * slope; // inlined scalb(x1_intercept, 32): final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept)) @@ -501,10 +497,10 @@ private void addLine(float x1, float y1, float x2, float y2) { private int windingRule; // Current drawing position, i.e., final point of last segment - private float x0, y0; + private double x0, y0; // Position of most recent 'moveTo' command - private float sx0, sy0; + private double sx0, sy0; // per-thread renderer context final RendererContext rdrCtx; @@ -594,8 +590,8 @@ Renderer init(final int pix_boundsX, final int pix_boundsY, edgeMinY = Integer.MAX_VALUE; edgeMaxY = Integer.MIN_VALUE; - edgeMinX = Float.POSITIVE_INFINITY; - edgeMaxX = Float.NEGATIVE_INFINITY; + edgeMinX = Double.POSITIVE_INFINITY; + edgeMaxX = Double.NEGATIVE_INFINITY; // reset used mark: edgeCount = 0; @@ -675,23 +671,23 @@ void dispose() { rdrCtx.stats.mon_rdr_endRendering.stop(); } // recycle the RendererContext instance - MarlinRenderingEngine.returnRendererContext(rdrCtx); + DMarlinRenderingEngine.returnRendererContext(rdrCtx); } - private static float tosubpixx(final float pix_x) { + private static double tosubpixx(final double pix_x) { return SUBPIXEL_SCALE_X * pix_x; } - private static float tosubpixy(final float pix_y) { + private static double tosubpixy(final double pix_y) { // shift y by -0.5 for fast ceil(y - 0.5): - return SUBPIXEL_SCALE_Y * pix_y - 0.5f; + return SUBPIXEL_SCALE_Y * pix_y - 0.5d; } @Override - public void moveTo(final float pix_x0, final float pix_y0) { + public void moveTo(final double pix_x0, final double pix_y0) { closePath(); - final float sx = tosubpixx(pix_x0); - final float sy = tosubpixy(pix_y0); + final double sx = tosubpixx(pix_x0); + final double sy = tosubpixy(pix_y0); this.sx0 = sx; this.sy0 = sy; this.x0 = sx; @@ -699,21 +695,21 @@ public void moveTo(final float pix_x0, final float pix_y0) { } @Override - public void lineTo(final float pix_x1, final float pix_y1) { - final float x1 = tosubpixx(pix_x1); - final float y1 = tosubpixy(pix_y1); + public void lineTo(final double pix_x1, final double pix_y1) { + final double x1 = tosubpixx(pix_x1); + final double y1 = tosubpixy(pix_y1); addLine(x0, y0, x1, y1); x0 = x1; y0 = y1; } @Override - public void curveTo(final float pix_x1, final float pix_y1, - final float pix_x2, final float pix_y2, - final float pix_x3, final float pix_y3) + public void curveTo(final double pix_x1, final double pix_y1, + final double pix_x2, final double pix_y2, + final double pix_x3, final double pix_y3) { - final float xe = tosubpixx(pix_x3); - final float ye = tosubpixy(pix_y3); + final double xe = tosubpixx(pix_x3); + final double ye = tosubpixy(pix_y3); curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), tosubpixx(pix_x2), tosubpixy(pix_y2), @@ -724,11 +720,11 @@ public void curveTo(final float pix_x1, final float pix_y1, } @Override - public void quadTo(final float pix_x1, final float pix_y1, - final float pix_x2, final float pix_y2) + public void quadTo(final double pix_x1, final double pix_y1, + final double pix_x2, final double pix_y2) { - final float xe = tosubpixx(pix_x2); - final float ye = tosubpixy(pix_y2); + final double xe = tosubpixx(pix_x2); + final double ye = tosubpixy(pix_y2); curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye); @@ -739,7 +735,7 @@ public void quadTo(final float pix_x1, final float pix_y1, @Override public void closePath() { - if (x0 != sx0 || y0 != sy0) { + if ((x0 != sx0) || (y0 != sy0)) { addLine(x0, y0, sx0, sy0); x0 = sx0; y0 = sy0; @@ -898,7 +894,7 @@ private void _endRendering(final int ymin, final int ymax) { rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd); } this.edgePtrs = _edgePtrs - = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // TODO FIX mark + = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // bad mark ? TODO: fix edge ptr mark ptrEnd); edgePtrsLen = _edgePtrs.length; @@ -1443,8 +1439,8 @@ boolean endRendering() { } // bounds as half-open intervals - final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5f), boundsMinX); - final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5f), boundsMaxX); + final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX); + final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX); // edge Min/Max Y are already rounded to subpixels within bounds: final int spminY = edgeMinY; diff --git a/src/main/java/sun/java2d/marlin/RendererContext.java b/src/main/java/sun/java2d/marlin/RendererContext.java index f27c599..216c97a 100644 --- a/src/main/java/sun/java2d/marlin/RendererContext.java +++ b/src/main/java/sun/java2d/marlin/RendererContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,14 +30,14 @@ import java.util.concurrent.atomic.AtomicInteger; import sun.java2d.ReentrantContext; import sun.java2d.marlin.ArrayCacheConst.CacheStats; -import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator; +import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator; import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer; import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter; /** * This class is a renderer context dedicated to a single thread */ -final class RendererContext extends ReentrantContext implements IRendererContext { +final class RendererContext extends ReentrantContext implements MarlinConst { // RendererContext creation counter private static final AtomicInteger CTX_COUNT = new AtomicInteger(1); @@ -48,16 +48,15 @@ final class RendererContext extends ReentrantContext implements IRendererContext * @return new RendererContext instance */ static RendererContext createContext() { - return new RendererContext("ctx" - + Integer.toString(CTX_COUNT.getAndIncrement())); + return new RendererContext("ctx" + CTX_COUNT.getAndIncrement()); } // Smallest object used as Cleaner's parent reference private final Object cleanerObj; - // dirty flag indicating an exception occured during pipeline in pathTo() + // dirty flag indicating an exception occurred during pipeline in pathTo() boolean dirty = false; // shared data - final float[] float6 = new float[6]; + final double[] double6 = new double[6]; // shared curve (dirty) (Renderer / Stroker) final Curve curve = new Curve(); // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter: @@ -67,7 +66,7 @@ static RendererContext createContext() { // MarlinRenderingEngine.TransformingPathConsumer2D final TransformingPathConsumer2D transformerPC2D; // TODO: clear if dirty ? // recycled Path2D instance (weak) - private WeakReference refPath2D = null; + private WeakReference refPath2D = null; final Renderer renderer; final Stroker stroker; // Simplifies out collinear lines @@ -84,9 +83,9 @@ static RendererContext createContext() { // flag indicating if the path is closed or not (in advance) to handle properly caps boolean closedPath = false; // clip rectangle (ymin, ymax, xmin, xmax): - final float[] clipRect = new float[4]; + final double[] clipRect = new double[4]; // clip inverse scale (mean) to adjust length checks - float clipInvScale = 0.0f; + double clipInvScale = 0.0d; // CurveBasicMonotonizer instance final CurveBasicMonotonizer monotonizer; // flag indicating to force the stroker to process joins @@ -101,14 +100,16 @@ static RendererContext createContext() { private final ArrayCacheIntClean cleanIntCache = new ArrayCacheIntClean(5); /* dirty int[] cache = 5 refs */ private final ArrayCacheInt dirtyIntCache = new ArrayCacheInt(5); - /* dirty float[] cache = 4 refs (2 polystack) */ - private final ArrayCacheFloat dirtyFloatCache = new ArrayCacheFloat(4); + /* dirty double[] cache = 4 refs (2 polystack) */ + private final ArrayCacheDouble dirtyDoubleCache = new ArrayCacheDouble(4); /* dirty byte[] cache = 2 ref (2 polystack) */ private final ArrayCacheByte dirtyByteCache = new ArrayCacheByte(2); // RendererContext statistics final RendererStats stats; + final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter(); + /** * Constructor * @@ -125,15 +126,15 @@ static RendererContext createContext() { stats = RendererStats.createInstance(cleanerObj, name); // push cache stats: stats.cacheStats = new CacheStats[] { cleanIntCache.stats, - dirtyIntCache.stats, dirtyFloatCache.stats, dirtyByteCache.stats + dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats }; } else { stats = null; } // NormalizingPathIterator instances: - nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6); - nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6); + nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6); + nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6); // curve monotonizer & clip subdivider (before transformerPC2D init) monotonizer = new CurveBasicMonotonizer(this); @@ -167,7 +168,7 @@ void dispose() { stroking = 0; doClip = false; closedPath = false; - clipInvScale = 0.0f; + clipInvScale = 0.0d; isFirstSegment = true; // if context is maked as DIRTY: @@ -188,37 +189,34 @@ void dispose() { } } - Path2D.Float getPath2D() { + Path2D.Double getPath2D() { // resolve reference: - Path2D.Float p2d = (refPath2D != null) ? refPath2D.get() : null; + Path2D.Double p2d = (refPath2D != null) ? refPath2D.get() : null; // create a new Path2D ? if (p2d == null) { - p2d = new Path2D.Float(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K + p2d = new Path2D.Double(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K // update weak reference: - refPath2D = new WeakReference(p2d); + refPath2D = new WeakReference(p2d); } // reset the path anyway: p2d.reset(); return p2d; } - @Override - public RendererStats stats() { + RendererStats stats() { return stats; } - @Override - public OffHeapArray newOffHeapArray(final long initialSize) { + OffHeapArray newOffHeapArray(final long initialSize) { if (DO_STATS) { stats.totalOffHeapInitial += initialSize; } return new OffHeapArray(cleanerObj, initialSize); } - @Override - public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) { + ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) { return cleanIntCache.createRef(initialSize); } @@ -226,11 +224,64 @@ ArrayCacheInt.Reference newDirtyIntArrayRef(final int initialSize) { return dirtyIntCache.createRef(initialSize); } - ArrayCacheFloat.Reference newDirtyFloatArrayRef(final int initialSize) { - return dirtyFloatCache.createRef(initialSize); + ArrayCacheDouble.Reference newDirtyDoubleArrayRef(final int initialSize) { + return dirtyDoubleCache.createRef(initialSize); } ArrayCacheByte.Reference newDirtyByteArrayRef(final int initialSize) { return dirtyByteCache.createRef(initialSize); } + + static final class PathConsumer2DAdapter implements DPathConsumer2D { + private sun.awt.geom.PathConsumer2D out; + + PathConsumer2DAdapter() {} + + PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) { + if (this.out != out) { + this.out = out; + } + return this; + } + + @Override + public void moveTo(double x0, double y0) { + out.moveTo((float)x0, (float)y0); + } + + @Override + public void lineTo(double x1, double y1) { + out.lineTo((float)x1, (float)y1); + } + + @Override + public void closePath() { + out.closePath(); + } + + @Override + public void pathDone() { + out.pathDone(); + } + + @Override + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + out.curveTo((float)x1, (float)y1, + (float)x2, (float)y2, + (float)x3, (float)y3); + } + + @Override + public void quadTo(double x1, double y1, double x2, double y2) { + out.quadTo((float)x1, (float)y1, (float)x2, (float)y2); + } + + @Override + public long getNativeConsumer() { + throw new InternalError("Not using a native peer"); + } + } } diff --git a/src/main/java/sun/java2d/marlin/Stroker.java b/src/main/java/sun/java2d/marlin/Stroker.java index 9795947..d781961 100644 --- a/src/main/java/sun/java2d/marlin/Stroker.java +++ b/src/main/java/sun/java2d/marlin/Stroker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,65 +25,63 @@ package sun.java2d.marlin; +import sun.java2d.marlin.debug.MarlinDebugThreadLocal; import java.util.Arrays; - -import sun.awt.geom.PathConsumer2D; import sun.java2d.marlin.Helpers.PolyStack; import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer; import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter; -import sun.java2d.marlin.debug.MarlinDebugThreadLocal; // TODO: some of the arithmetic here is too verbose and prone to hard to // debug typos. We should consider making a small Point/Vector class that // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such -final class Stroker implements PathConsumer2D, MarlinConst { +final class Stroker implements DPathConsumer2D, MarlinConst { private static final int MOVE_TO = 0; private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad private static final int CLOSE = 2; // round join threshold = 1 subpixel - private static final float ERR_JOIN = (1.0f / MIN_SUBPIXELS); - private static final float ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN; + private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS); + private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN; // kappa = (4/3) * (SQRT(2) - 1) - private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d); + private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d); // SQRT(2) - private static final float SQRT_2 = (float)Math.sqrt(2.0d); + private static final double SQRT_2 = Math.sqrt(2.0d); - private PathConsumer2D out; + private DPathConsumer2D out; private int capStyle; private int joinStyle; - private float lineWidth2; - private float invHalfLineWidth2Sq; + private double lineWidth2; + private double invHalfLineWidth2Sq; - private final float[] offset0 = new float[2]; - private final float[] offset1 = new float[2]; - private final float[] offset2 = new float[2]; - private final float[] miter = new float[2]; - private float miterLimitSq; + private final double[] offset0 = new double[2]; + private final double[] offset1 = new double[2]; + private final double[] offset2 = new double[2]; + private final double[] miter = new double[2]; + private double miterLimitSq; private int prev; // The starting point of the path, and the slope there. - private float sx0, sy0, sdx, sdy; + private double sx0, sy0, sdx, sdy; // the current point and the slope there. - private float cx0, cy0, cdx, cdy; // c stands for current + private double cx0, cy0, cdx, cdy; // c stands for current // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the // first and last points on the left parallel path. Since this path is // parallel, it's slope at any point is parallel to the slope of the // original path (thought they may have different directions), so these // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that // would be error prone and hard to read, so we keep these anyway. - private float smx, smy, cmx, cmy; + private double smx, smy, cmx, cmy; private final PolyStack reverse; - private final float[] lp = new float[8]; - private final float[] rp = new float[8]; + private final double[] lp = new double[8]; + private final double[] rp = new double[8]; // per-thread renderer context final RendererContext rdrCtx; @@ -92,7 +90,7 @@ final class Stroker implements PathConsumer2D, MarlinConst { final Curve curve; // Bounds of the drawing region, at pixel precision. - private float[] clipRect; + private double[] clipRect; // the outcode of the current point private int cOutCode = 0; @@ -107,7 +105,7 @@ final class Stroker implements PathConsumer2D, MarlinConst { // flag indicating to monotonize curves private boolean monotonize; - private boolean subdivide = DO_CLIP_SUBDIVIDER; + private boolean subdivide = false; private final CurveClipSplitter curveSplitter; /** @@ -133,7 +131,7 @@ final class Stroker implements PathConsumer2D, MarlinConst { /** * Inits the Stroker. * - * @param pc2d an output PathConsumer2D. + * @param pc2d an output DPathConsumer2D. * @param lineWidth the desired line width in pixels * @param capStyle the desired end cap style, one of * CAP_BUTT, CAP_ROUND or @@ -145,25 +143,25 @@ final class Stroker implements PathConsumer2D, MarlinConst { * @param subdivideCurves true to indicate to subdivide curves, false if dasher does * @return this instance */ - Stroker init(final PathConsumer2D pc2d, - final float lineWidth, - final int capStyle, - final int joinStyle, - final float miterLimit, - final boolean subdivideCurves) + Stroker init(final DPathConsumer2D pc2d, + final double lineWidth, + final int capStyle, + final int joinStyle, + final double miterLimit, + final boolean subdivideCurves) { if (this.out != pc2d) { this.out = pc2d; } - this.lineWidth2 = lineWidth / 2.0f; - this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2); + this.lineWidth2 = lineWidth / 2.0d; + this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2); this.monotonize = subdivideCurves; this.capStyle = capStyle; this.joinStyle = joinStyle; - final float limit = miterLimit * lineWidth2; + final double limit = miterLimit * lineWidth2; this.miterLimitSq = limit * limit; this.prev = CLOSE; @@ -172,7 +170,7 @@ Stroker init(final PathConsumer2D pc2d, if (rdrCtx.doClip) { // Adjust the clipping rectangle with the stroker margin (miter limit, width) - float margin = lineWidth2; + double margin = lineWidth2; if (capStyle == CAP_SQUARE) { margin *= SQRT_2; @@ -183,7 +181,7 @@ Stroker init(final PathConsumer2D pc2d, // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY // adjust clip rectangle (ymin, ymax, xmin, xmax): - final float[] _clipRect = rdrCtx.clipRect; + final double[] _clipRect = rdrCtx.clipRect; _clipRect[0] -= margin; _clipRect[1] += margin; _clipRect[2] -= margin; @@ -229,24 +227,24 @@ void dispose() { if (DO_CLEAN_DIRTY) { // Force zero-fill dirty arrays: - Arrays.fill(offset0, 0.0f); - Arrays.fill(offset1, 0.0f); - Arrays.fill(offset2, 0.0f); - Arrays.fill(miter, 0.0f); - Arrays.fill(lp, 0.0f); - Arrays.fill(rp, 0.0f); + Arrays.fill(offset0, 0.0d); + Arrays.fill(offset1, 0.0d); + Arrays.fill(offset2, 0.0d); + Arrays.fill(miter, 0.0d); + Arrays.fill(lp, 0.0d); + Arrays.fill(rp, 0.0d); } } - private static void computeOffset(final float lx, final float ly, - final float w, final float[] m) + private static void computeOffset(final double lx, final double ly, + final double w, final double[] m) { - float len = lx*lx + ly*ly; - if (len == 0.0f) { - m[0] = 0.0f; - m[1] = 0.0f; + double len = lx*lx + ly*ly; + if (len == 0.0d) { + m[0] = 0.0d; + m[1] = 0.0d; } else { - len = (float) Math.sqrt(len); + len = Math.sqrt(len); m[0] = (ly * w) / len; m[1] = -(lx * w) / len; } @@ -260,24 +258,24 @@ private static void computeOffset(final float lx, final float ly, // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a // clockwise order. // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left. - private static boolean isCW(final float dx1, final float dy1, - final float dx2, final float dy2) + private static boolean isCW(final double dx1, final double dy1, + final double dx2, final double dy2) { return dx1 * dy2 <= dy1 * dx2; } - private void mayDrawRoundJoin(float cx, float cy, - float omx, float omy, - float mx, float my, + private void mayDrawRoundJoin(double cx, double cy, + double omx, double omy, + double mx, double my, boolean rev) { - if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) { + if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) { return; } - final float domx = omx - mx; - final float domy = omy - my; - final float lenSq = domx*domx + domy*domy; + final double domx = omx - mx; + final double domy = omy - my; + final double lenSq = domx*domx + domy*domy; if (lenSq < ROUND_JOIN_THRESHOLD) { return; @@ -292,19 +290,19 @@ private void mayDrawRoundJoin(float cx, float cy, drawRoundJoin(cx, cy, omx, omy, mx, my, rev); } - private void drawRoundJoin(float cx, float cy, - float omx, float omy, - float mx, float my, + private void drawRoundJoin(double cx, double cy, + double omx, double omy, + double mx, double my, boolean rev) { // The sign of the dot product of mx,my and omx,omy is equal to the // the sign of the cosine of ext // (ext is the angle between omx,omy and mx,my). - final float cosext = omx * mx + omy * my; + final double cosext = omx * mx + omy * my; // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only // need 1 curve to approximate the circle section that joins omx,omy // and mx,my. - if (cosext >= 0.0f) { + if (cosext >= 0.0d) { drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev); } else { // we need to split the arc into 2 arcs spanning the same angle. @@ -321,10 +319,10 @@ private void drawRoundJoin(float cx, float cy, // have numerical problems because we know that lineWidth2 divided by // this normal's length is at least 0.5 and at most sqrt(2)/2 (because // we know the angle of the arc is > 90 degrees). - float nx = my - omy, ny = omx - mx; - float nlen = (float) Math.sqrt(nx*nx + ny*ny); - float scale = lineWidth2/nlen; - float mmx = nx * scale, mmy = ny * scale; + double nx = my - omy, ny = omx - mx; + double nlen = Math.sqrt(nx*nx + ny*ny); + double scale = lineWidth2/nlen; + double mmx = nx * scale, mmy = ny * scale; // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've // computed the wrong intersection so we get the other one. @@ -339,16 +337,16 @@ private void drawRoundJoin(float cx, float cy, } // the input arc defined by omx,omy and mx,my must span <= 90 degrees. - private void drawBezApproxForArc(final float cx, final float cy, - final float omx, final float omy, - final float mx, final float my, + private void drawBezApproxForArc(final double cx, final double cy, + final double omx, final double omy, + final double mx, final double my, boolean rev) { - final float cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq; + final double cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq; // check round off errors producing cos(ext) > 1 and a NaN below // cos(ext) == 1 implies colinear segments and an empty join anyway - if (cosext2 >= 0.5f) { + if (cosext2 >= 0.5d) { // just return to avoid generating a flat curve: return; } @@ -358,28 +356,28 @@ private void drawBezApproxForArc(final float cx, final float cy, // define the bezier curve we're computing. // It is computed using the constraints that P1-P0 and P3-P2 are parallel // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|. - float cv = (float) ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) / + double cv = ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) / (1.0d + Math.sqrt(cosext2 + 0.5d))); // if clockwise, we need to negate cv. if (rev) { // rev is equivalent to isCW(omx, omy, mx, my) cv = -cv; } - final float x1 = cx + omx; - final float y1 = cy + omy; - final float x2 = x1 - cv * omy; - final float y2 = y1 + cv * omx; + final double x1 = cx + omx; + final double y1 = cy + omy; + final double x2 = x1 - cv * omy; + final double y2 = y1 + cv * omx; - final float x4 = cx + mx; - final float y4 = cy + my; - final float x3 = x4 + cv * my; - final float y3 = y4 - cv * mx; + final double x4 = cx + mx; + final double y4 = cy + my; + final double x3 = x4 + cv * my; + final double y3 = y4 - cv * mx; emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev); } - private void drawRoundCap(float cx, float cy, float mx, float my) { - final float Cmx = C * mx; - final float Cmy = C * my; + private void drawRoundCap(double cx, double cy, double mx, double my) { + final double Cmx = C * mx; + final double Cmy = C * my; emitCurveTo(cx + mx - Cmy, cy + my + Cmx, cx - my + Cmx, cy + mx + Cmy, cx - my, cy + mx); @@ -390,16 +388,16 @@ private void drawRoundCap(float cx, float cy, float mx, float my) { // Return the intersection point of the lines (x0, y0) -> (x1, y1) // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] - private static void computeMiter(final float x0, final float y0, - final float x1, final float y1, - final float x0p, final float y0p, - final float x1p, final float y1p, - final float[] m) + private static void computeMiter(final double x0, final double y0, + final double x1, final double y1, + final double x0p, final double y0p, + final double x1p, final double y1p, + final double[] m) { - float x10 = x1 - x0; - float y10 = y1 - y0; - float x10p = x1p - x0p; - float y10p = y1p - y0p; + double x10 = x1 - x0; + double y10 = y1 - y0; + double x10p = x1p - x0p; + double y10p = y1p - y0p; // if this is 0, the lines are parallel. If they go in the // same direction, there is no intersection so m[off] and @@ -410,8 +408,8 @@ private static void computeMiter(final float x0, final float y0, // miter drawing because it won't be called by drawMiter (because // (mx == omx && my == omy) will be true, and drawMiter will return // immediately). - float den = x10*y10p - x10p*y10; - float t = x10p*(y0-y0p) - y10p*(x0-x0p); + double den = x10*y10p - x10p*y10; + double t = x10p*(y0-y0p) - y10p*(x0-x0p); t /= den; m[0] = x0 + t*x10; m[1] = y0 + t*y10; @@ -419,16 +417,16 @@ private static void computeMiter(final float x0, final float y0, // Return the intersection point of the lines (x0, y0) -> (x1, y1) // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1] - private static void safeComputeMiter(final float x0, final float y0, - final float x1, final float y1, - final float x0p, final float y0p, - final float x1p, final float y1p, - final float[] m) + private static void safeComputeMiter(final double x0, final double y0, + final double x1, final double y1, + final double x0p, final double y0p, + final double x1p, final double y1p, + final double[] m) { - float x10 = x1 - x0; - float y10 = y1 - y0; - float x10p = x1p - x0p; - float y10p = y1p - y0p; + double x10 = x1 - x0; + double y10 = y1 - y0; + double x10p = x1p - x0p; + double y10p = y1p - y0p; // if this is 0, the lines are parallel. If they go in the // same direction, there is no intersection so m[off] and @@ -439,28 +437,28 @@ private static void safeComputeMiter(final float x0, final float y0, // miter drawing because it won't be called by drawMiter (because // (mx == omx && my == omy) will be true, and drawMiter will return // immediately). - float den = x10*y10p - x10p*y10; - if (den == 0.0f) { - m[2] = (x0 + x0p) / 2.0f; - m[3] = (y0 + y0p) / 2.0f; + double den = x10*y10p - x10p*y10; + if (den == 0.0d) { + m[2] = (x0 + x0p) / 2.0d; + m[3] = (y0 + y0p) / 2.0d; } else { - float t = x10p*(y0-y0p) - y10p*(x0-x0p); + double t = x10p*(y0-y0p) - y10p*(x0-x0p); t /= den; m[2] = x0 + t*x10; m[3] = y0 + t*y10; } } - private void drawMiter(final float pdx, final float pdy, - final float x0, final float y0, - final float dx, final float dy, - float omx, float omy, - float mx, float my, + private void drawMiter(final double pdx, final double pdy, + final double x0, final double y0, + final double dx, final double dy, + double omx, double omy, + double mx, double my, boolean rev) { if ((mx == omx && my == omy) || - (pdx == 0.0f && pdy == 0.0f) || - (dx == 0.0f && dy == 0.0f)) + (pdx == 0.0d && pdy == 0.0d) || + (dx == 0.0d && dy == 0.0d)) { return; } @@ -475,9 +473,9 @@ private void drawMiter(final float pdx, final float pdy, computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter); - final float miterX = miter[0]; - final float miterY = miter[1]; - float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0); + final double miterX = miter[0]; + final double miterY = miter[1]; + double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0); // If the lines are parallel, lenSq will be either NaN or +inf // (actually, I'm not sure if the latter is possible. The important @@ -490,13 +488,13 @@ private void drawMiter(final float pdx, final float pdy, } @Override - public void moveTo(final float x0, final float y0) { + public void moveTo(final double x0, final double y0) { _moveTo(x0, y0, cOutCode); // update starting point: this.sx0 = x0; this.sy0 = y0; - this.sdx = 1.0f; - this.sdy = 0.0f; + this.sdx = 1.0d; + this.sdy = 0.0d; this.opened = false; this.capStart = false; @@ -507,7 +505,7 @@ public void moveTo(final float x0, final float y0) { } } - private void _moveTo(final float x0, final float y0, + private void _moveTo(final double x0, final double y0, final int outcode) { if (prev == MOVE_TO) { @@ -520,13 +518,13 @@ private void _moveTo(final float x0, final float y0, this.prev = MOVE_TO; this.cx0 = x0; this.cy0 = y0; - this.cdx = 1.0f; - this.cdy = 0.0f; + this.cdx = 1.0d; + this.cdy = 0.0d; } } @Override - public void lineTo(final float x1, final float y1) { + public void lineTo(final double x1, final double y1) { final int outcode0 = this.cOutCode; if (clipRect != null) { @@ -564,14 +562,14 @@ public void lineTo(final float x1, final float y1) { this.cOutCode = outcode1; } - float dx = x1 - cx0; - float dy = y1 - cy0; - if (dx == 0.0f && dy == 0.0f) { - dx = 1.0f; + double dx = x1 - cx0; + double dy = y1 - cy0; + if (dx == 0.0d && dy == 0.0d) { + dx = 1.0d; } computeOffset(dx, dy, lineWidth2, offset0); - final float mx = offset0[0]; - final float my = offset0[1]; + final double mx = offset0[0]; + final double my = offset0[1]; drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0); @@ -599,14 +597,14 @@ public void closePath() { } emitMoveTo(cx0, cy0 - lineWidth2); - this.sdx = 1.0f; - this.sdy = 0.0f; - this.cdx = 1.0f; - this.cdy = 0.0f; + this.sdx = 1.0d; + this.sdy = 0.0d; + this.cdx = 1.0d; + this.cdy = 0.0d; - this.smx = 0.0f; + this.smx = 0.0d; this.smy = -lineWidth2; - this.cmx = 0.0f; + this.cmx = 0.0d; this.cmy = -lineWidth2; finish(cOutCode); @@ -615,7 +613,7 @@ public void closePath() { // basic acceptance criteria if ((sOutCode & cOutCode) == 0) { - if (cx0 != sx0 || cy0 != sy0) { + if ((cx0 != sx0) || (cy0 != sy0)) { // may subdivide line: lineTo(sx0, sy0); } @@ -705,19 +703,19 @@ private void finish(final int outcode) { emitClose(); } - private void emitMoveTo(final float x0, final float y0) { + private void emitMoveTo(final double x0, final double y0) { out.moveTo(x0, y0); } - private void emitLineTo(final float x1, final float y1) { + private void emitLineTo(final double x1, final double y1) { out.lineTo(x1, y1); } - private void emitLineToRev(final float x1, final float y1) { + private void emitLineToRev(final double x1, final double y1) { reverse.pushLine(x1, y1); } - private void emitLineTo(final float x1, final float y1, + private void emitLineTo(final double x1, final double y1, final boolean rev) { if (rev) { @@ -727,36 +725,36 @@ private void emitLineTo(final float x1, final float y1, } } - private void emitQuadTo(final float x1, final float y1, - final float x2, final float y2) + private void emitQuadTo(final double x1, final double y1, + final double x2, final double y2) { out.quadTo(x1, y1, x2, y2); } - private void emitQuadToRev(final float x0, final float y0, - final float x1, final float y1) + private void emitQuadToRev(final double x0, final double y0, + final double x1, final double y1) { reverse.pushQuad(x0, y0, x1, y1); } - private void emitCurveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + private void emitCurveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { out.curveTo(x1, y1, x2, y2, x3, y3); } - private void emitCurveToRev(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2) + private void emitCurveToRev(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2) { reverse.pushCubic(x0, y0, x1, y1, x2, y2); } - private void emitCurveTo(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3, final boolean rev) + private void emitCurveTo(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3, final boolean rev) { if (rev) { reverse.pushCubic(x0, y0, x1, y1, x2, y2); @@ -769,11 +767,11 @@ private void emitClose() { out.closePath(); } - private void drawJoin(float pdx, float pdy, - float x0, float y0, - float dx, float dy, - float omx, float omy, - float mx, float my, + private void drawJoin(double pdx, double pdy, + double x0, double y0, + double dx, double dy, + double omx, double omy, + double mx, double my, final int outcode) { if (prev != DRAWING_OP_TO) { @@ -800,17 +798,16 @@ private void drawJoin(float pdx, float pdy, // reset trigger to process further joins (normal operations) rdrCtx.isFirstSegment = true; } - prev = DRAWING_OP_TO; } - private int getLineOffsets(final float x1, final float y1, - final float x2, final float y2, - final float[] left, final float[] right) + private int getLineOffsets(final double x1, final double y1, + final double x2, final double y2, + final double[] left, final double[] right) { computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0); - final float mx = offset0[0]; - final float my = offset0[1]; + final double mx = offset0[0]; + final double my = offset0[1]; left[0] = x1 + mx; left[1] = y1 + my; left[2] = x2 + mx; @@ -824,9 +821,9 @@ private int getLineOffsets(final float x1, final float y1, return 4; } - private int computeOffsetCubic(final float[] pts, final int off, - final float[] leftOff, - final float[] rightOff) + private int computeOffsetCubic(final double[] pts, final int off, + final double[] leftOff, + final double[] rightOff) { // if p1=p2 or p3=p4 it means that the derivative at the endpoint // vanishes, which creates problems with computeOffset. Usually @@ -835,18 +832,18 @@ private int computeOffsetCubic(final float[] pts, final int off, // the input curve at the cusp, and passes it to this function. // because of inaccuracies in the splitting, we consider points // equal if they're very close to each other. - final float x1 = pts[off ]; final float y1 = pts[off + 1]; - final float x2 = pts[off + 2]; final float y2 = pts[off + 3]; - final float x3 = pts[off + 4]; final float y3 = pts[off + 5]; - final float x4 = pts[off + 6]; final float y4 = pts[off + 7]; + final double x1 = pts[off ]; final double y1 = pts[off + 1]; + final double x2 = pts[off + 2]; final double y2 = pts[off + 3]; + final double x3 = pts[off + 4]; final double y3 = pts[off + 5]; + final double x4 = pts[off + 6]; final double y4 = pts[off + 7]; - float dx1 = x2 - x1; float dy1 = y2 - y1; - float dx4 = x4 - x3; float dy4 = y4 - y3; + double dx1 = x2 - x1; double dy1 = y2 - y1; + double dx4 = x4 - x3; double dy4 = y4 - y3; // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, // in which case ignore if p1 == p2 - final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0f * Math.ulp(y2)); - final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0f * Math.ulp(y4)); + final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2)); + final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4)); if (p1eqp2 && p3eqp4) { return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); @@ -859,12 +856,12 @@ private int computeOffsetCubic(final float[] pts, final int off, } // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line - float dotsq = (dx1 * dx4 + dy1 * dy4); + double dotsq = (dx1 * dx4 + dy1 * dy4); dotsq *= dotsq; - final float l1sq = dx1 * dx1 + dy1 * dy1; - final float l4sq = dx4 * dx4 + dy4 * dy4; + final double l1sq = dx1 * dx1 + dy1 * dy1; + final double l4sq = dx4 * dx4 + dy4 * dy4; - if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) { + if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) { return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); } @@ -915,12 +912,12 @@ private int computeOffsetCubic(final float[] pts, final int off, // getting the inverse of the matrix above. Then we use [c1,c2] to compute // p2p and p3p. - final float xm = (x1 + x4 + 3.0f * (x2 + x3)) / 8.0f; - final float ym = (y1 + y4 + 3.0f * (y2 + y3)) / 8.0f; + final double xm = (x1 + x4 + 3.0d * (x2 + x3)) / 8.0d; + final double ym = (y1 + y4 + 3.0d * (y2 + y3)) / 8.0d; // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to // c*B'(0.5) for some constant c. - final float dxm = x3 + x4 - (x1 + x2); - final float dym = y3 + y4 - (y1 + y2); + final double dxm = x3 + x4 - (x1 + x2); + final double dym = y3 + y4 - (y1 + y2); // this computes the offsets at t=0, 0.5, 1, using the property that // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to @@ -930,12 +927,12 @@ private int computeOffsetCubic(final float[] pts, final int off, computeOffset(dx4, dy4, lineWidth2, offset2); // left side: - float x1p = x1 + offset0[0]; // start - float y1p = y1 + offset0[1]; // point - float xi = xm + offset1[0]; // interpolation - float yi = ym + offset1[1]; // point - float x4p = x4 + offset2[0]; // end - float y4p = y4 + offset2[1]; // point + double x1p = x1 + offset0[0]; // start + double y1p = y1 + offset0[1]; // point + double xi = xm + offset1[0]; // interpolation + double yi = ym + offset1[1]; // point + double x4p = x4 + offset2[0]; // end + double y4p = y4 + offset2[1]; // point if (false) { final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get(); @@ -943,15 +940,15 @@ private int computeOffsetCubic(final float[] pts, final int off, dbgCtx.addPoint(xi, yi); } - final float invdet43 = 4.0f / (3.0f * (dx1 * dy4 - dy1 * dx4)); + final double invdet43 = 4.0d / (3.0d * (dx1 * dy4 - dy1 * dx4)); - float two_pi_m_p1_m_p4x = 2.0f * xi - (x1p + x4p); - float two_pi_m_p1_m_p4y = 2.0f * yi - (y1p + y4p); + double two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p); + double two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p); - float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); - float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); + double c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); + double c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); - float x2p, y2p, x3p, y3p; + double x2p, y2p, x3p, y3p; if (c1 * c2 > 0.0) { // System.out.println("Buggy solver (left): c1 = " + c1 + " c2 = " + c2); @@ -992,8 +989,8 @@ private int computeOffsetCubic(final float[] pts, final int off, dbgCtx.addPoint(xi, yi); } - two_pi_m_p1_m_p4x = 2.0f * xi - (x1p + x4p); - two_pi_m_p1_m_p4y = 2.0f * yi - (y1p + y4p); + two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p); + two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p); c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); @@ -1029,24 +1026,24 @@ private int computeOffsetCubic(final float[] pts, final int off, // compute offset curves using bezier spline through t=0.5 (i.e. // ComputedCurve(0.5) == IdealParallelCurve(0.5)) // return the kind of curve in the right and left arrays. - private int computeOffsetQuad(final float[] pts, final int off, - final float[] leftOff, - final float[] rightOff) + private int computeOffsetQuad(final double[] pts, final int off, + final double[] leftOff, + final double[] rightOff) { return computeOffsetQuad(pts, off, leftOff, rightOff, true); } - private int computeOffsetQuad(final float[] pts, final int off, - final float[] leftOff, - final float[] rightOff, + private int computeOffsetQuad(final double[] pts, final int off, + final double[] leftOff, + final double[] rightOff, final boolean checkCtrlPoints) { - final float x1 = pts[off ]; final float y1 = pts[off + 1]; - final float x2 = pts[off + 2]; final float y2 = pts[off + 3]; - final float x3 = pts[off + 4]; final float y3 = pts[off + 5]; + final double x1 = pts[off ]; final double y1 = pts[off + 1]; + final double x2 = pts[off + 2]; final double y2 = pts[off + 3]; + final double x3 = pts[off + 4]; final double y3 = pts[off + 5]; - final float dx12 = x2 - x1; final float dy12 = y2 - y1; - final float dx23 = x3 - x2; final float dy23 = y3 - y2; + final double dx12 = x2 - x1; final double dy12 = y2 - y1; + final double dx23 = x3 - x2; final double dy23 = y3 - y2; if (checkCtrlPoints) { // if p1=p2 or p2=p3 it means that the derivative at the endpoint @@ -1058,20 +1055,20 @@ private int computeOffsetQuad(final float[] pts, final int off, // equal if they're very close to each other. // if p1 == p2 or p2 == p3: draw line from p1->p3 - final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0f * Math.ulp(y2)); - final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0f * Math.ulp(y3)); + final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2)); + final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3)); if (p1eqp2 || p2eqp3) { return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); } // if p2-p1 and p3-p2 are parallel, that must mean this curve is a line - float dotsq = (dx12 * dx23 + dy12 * dy23); + double dotsq = (dx12 * dx23 + dy12 * dy23); dotsq *= dotsq; - final float l1sq = dx12 * dx12 + dy12 * dy12; - final float l3sq = dx23 * dx23 + dy23 * dy23; + final double l1sq = dx12 * dx12 + dy12 * dy12; + final double l3sq = dx23 * dx23 + dy23 * dy23; - if (Helpers.within(dotsq, l1sq * l3sq, 4.0f * Math.ulp(dotsq))) { + if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) { return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); } } @@ -1082,10 +1079,10 @@ private int computeOffsetQuad(final float[] pts, final int off, computeOffset(dx12, dy12, lineWidth2, offset0); computeOffset(dx23, dy23, lineWidth2, offset1); - float x1p = x1 + offset0[0]; // start - float y1p = y1 + offset0[1]; // point - float x3p = x3 + offset1[0]; // end - float y3p = y3 + offset1[1]; // point + double x1p = x1 + offset0[0]; // start + double y1p = y1 + offset0[1]; // point + double x3p = x3 + offset1[0]; // end + double y3p = y3 + offset1[1]; // point safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, leftOff); leftOff[0] = x1p; leftOff[1] = y1p; @@ -1104,9 +1101,9 @@ private int computeOffsetQuad(final float[] pts, final int off, } @Override - public void curveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + public void curveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { final int outcode0 = this.cOutCode; @@ -1150,34 +1147,34 @@ public void curveTo(final float x1, final float y1, _curveTo(x1, y1, x2, y2, x3, y3, outcode0); } - private void _curveTo(final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3, + private void _curveTo(final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3, final int outcode0) { // need these so we can update the state at the end of this method - float dxs = x1 - cx0; - float dys = y1 - cy0; - float dxf = x3 - x2; - float dyf = y3 - y2; + double dxs = x1 - cx0; + double dys = y1 - cy0; + double dxf = x3 - x2; + double dyf = y3 - y2; - if ((dxs == 0.0f) && (dys == 0.0f)) { + if ((dxs == 0.0d) && (dys == 0.0d)) { dxs = x2 - cx0; dys = y2 - cy0; - if ((dxs == 0.0f) && (dys == 0.0f)) { + if ((dxs == 0.0d) && (dys == 0.0d)) { dxs = x3 - cx0; dys = y3 - cy0; } } - if ((dxf == 0.0f) && (dyf == 0.0f)) { + if ((dxf == 0.0d) && (dyf == 0.0d)) { dxf = x3 - x1; dyf = y3 - y1; - if ((dxf == 0.0f) && (dyf == 0.0f)) { + if ((dxf == 0.0d) && (dyf == 0.0d)) { dxf = x3 - cx0; dyf = y3 - cy0; } } - if ((dxs == 0.0f) && (dys == 0.0f)) { + if ((dxs == 0.0d) && (dys == 0.0d)) { // this happens if the "curve" is just a point // fix outcode0 for lineTo() call: if (clipRect != null) { @@ -1189,13 +1186,13 @@ private void _curveTo(final float x1, final float y1, // if these vectors are too small, normalize them, to avoid future // precision problems. - if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) { - final float len = (float)Math.sqrt(dxs * dxs + dys * dys); + if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { + final double len = Math.sqrt(dxs * dxs + dys * dys); dxs /= len; dys /= len; } - if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) { - final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf); + if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { + final double len = Math.sqrt(dxf * dxf + dyf * dyf); dxf /= len; dyf /= len; } @@ -1204,8 +1201,8 @@ private void _curveTo(final float x1, final float y1, drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); int nSplits = 0; - final float[] mid; - final float[] l = lp; + final double[] mid; + final double[] l = lp; if (monotonize) { // monotonize curve: @@ -1222,7 +1219,7 @@ private void _curveTo(final float x1, final float y1, mid[4] = x2; mid[5] = y2; mid[6] = x3; mid[7] = y3; } - final float[] r = rp; + final double[] r = rp; int kind = 0; for (int i = 0, off = 0; i <= nSplits; i++, off += 6) { @@ -1249,13 +1246,13 @@ private void _curveTo(final float x1, final float y1, this.cy0 = y3; this.cdx = dxf; this.cdy = dyf; - this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f; - this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f; + this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; + this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; } @Override - public void quadTo(final float x1, final float y1, - final float x2, final float y2) + public void quadTo(final double x1, final double y1, + final double x2, final double y2) { final int outcode0 = this.cOutCode; @@ -1297,21 +1294,21 @@ public void quadTo(final float x1, final float y1, _quadTo(x1, y1, x2, y2, outcode0); } - private void _quadTo(final float x1, final float y1, - final float x2, final float y2, - final int outcode0) + private void _quadTo(final double x1, final double y1, + final double x2, final double y2, + final int outcode0) { // need these so we can update the state at the end of this method - float dxs = x1 - cx0; - float dys = y1 - cy0; - float dxf = x2 - x1; - float dyf = y2 - y1; + double dxs = x1 - cx0; + double dys = y1 - cy0; + double dxf = x2 - x1; + double dyf = y2 - y1; - if (((dxs == 0.0f) && (dys == 0.0f)) || ((dxf == 0.0f) && (dyf == 0.0f))) { + if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) { dxs = dxf = x2 - cx0; dys = dyf = y2 - cy0; } - if ((dxs == 0.0f) && (dys == 0.0f)) { + if ((dxs == 0.0d) && (dys == 0.0d)) { // this happens if the "curve" is just a point // fix outcode0 for lineTo() call: if (clipRect != null) { @@ -1322,13 +1319,13 @@ private void _quadTo(final float x1, final float y1, } // if these vectors are too small, normalize them, to avoid future // precision problems. - if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) { - final float len = (float)Math.sqrt(dxs * dxs + dys * dys); + if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) { + final double len = Math.sqrt(dxs * dxs + dys * dys); dxs /= len; dys /= len; } - if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) { - final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf); + if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) { + final double len = Math.sqrt(dxf * dxf + dyf * dyf); dxf /= len; dyf /= len; } @@ -1336,8 +1333,8 @@ private void _quadTo(final float x1, final float y1, drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0); int nSplits = 0; - final float[] mid; - final float[] l = lp; + final double[] mid; + final double[] l = lp; if (monotonize) { // monotonize quad: @@ -1353,7 +1350,7 @@ private void _quadTo(final float x1, final float y1, mid[2] = x1; mid[3] = y1; mid[4] = x2; mid[5] = y2; } - final float[] r = rp; + final double[] r = rp; int kind = 0; for (int i = 0, off = 0; i <= nSplits; i++, off += 4) { @@ -1380,8 +1377,8 @@ private void _quadTo(final float x1, final float y1, this.cy0 = y2; this.cdx = dxf; this.cdy = dyf; - this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f; - this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f; + this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d; + this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d; } @Override public long getNativeConsumer() { diff --git a/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java b/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java index 1262d01..d540472 100644 --- a/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java +++ b/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package sun.java2d.marlin; -import sun.awt.geom.PathConsumer2D; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.util.Arrays; @@ -34,8 +33,8 @@ final class TransformingPathConsumer2D { - // higher uncertainty in float variant for huge shapes > 10^7 - static final float CLIP_RECT_PADDING = 1.0f; + // smaller uncertainty in double variant + static final double CLIP_RECT_PADDING = 0.25d; private final RendererContext rdrCtx; @@ -45,14 +44,14 @@ final class TransformingPathConsumer2D { // recycled PathClipFilter instance from pathClipper() private final PathClipFilter pathClipper; - // recycled PathConsumer2D instance from wrapPath2D() + // recycled DPathConsumer2D instance from wrapPath2D() private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); - // recycled PathConsumer2D instances from deltaTransformConsumer() + // recycled DPathConsumer2D instances from deltaTransformConsumer() private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); - // recycled PathConsumer2D instances from inverseDeltaTransformConsumer() + // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer() private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); @@ -70,51 +69,51 @@ final class TransformingPathConsumer2D { this.pathClipper = new PathClipFilter(rdrCtx); } - PathConsumer2D wrapPath2D(Path2D.Float p2d) { + DPathConsumer2D wrapPath2D(Path2D.Double p2d) { return wp_Path2DWrapper.init(p2d); } - PathConsumer2D traceInput(PathConsumer2D out) { + DPathConsumer2D traceInput(DPathConsumer2D out) { return tracerInput.init(out); } - PathConsumer2D traceClosedPathDetector(PathConsumer2D out) { + DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) { return tracerCPDetector.init(out); } - PathConsumer2D traceFiller(PathConsumer2D out) { + DPathConsumer2D traceFiller(DPathConsumer2D out) { return tracerFiller.init(out); } - PathConsumer2D traceStroker(PathConsumer2D out) { + DPathConsumer2D traceStroker(DPathConsumer2D out) { return tracerStroker.init(out); } - PathConsumer2D traceDasher(PathConsumer2D out) { + DPathConsumer2D traceDasher(DPathConsumer2D out) { return tracerDasher.init(out); } - PathConsumer2D detectClosedPath(PathConsumer2D out) { + DPathConsumer2D detectClosedPath(DPathConsumer2D out) { return cpDetector.init(out); } - PathConsumer2D pathClipper(PathConsumer2D out) { + DPathConsumer2D pathClipper(DPathConsumer2D out) { return pathClipper.init(out); } - PathConsumer2D deltaTransformConsumer(PathConsumer2D out, + DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, AffineTransform at) { if (at == null) { return out; } - final float mxx = (float) at.getScaleX(); - final float mxy = (float) at.getShearX(); - final float myx = (float) at.getShearY(); - final float myy = (float) at.getScaleY(); + final double mxx = at.getScaleX(); + final double mxy = at.getShearX(); + final double myx = at.getShearY(); + final double myy = at.getScaleY(); - if (mxy == 0.0f && myx == 0.0f) { - if (mxx == 1.0f && myy == 1.0f) { + if (mxy == 0.0d && myx == 0.0d) { + if (mxx == 1.0d && myy == 1.0d) { return out; } else { // Scale only @@ -135,26 +134,26 @@ PathConsumer2D deltaTransformConsumer(PathConsumer2D out, } } - private static float adjustClipScale(final float[] clipRect, - final float mxx, final float myy) + private static double adjustClipScale(final double[] clipRect, + final double mxx, final double myy) { // Adjust the clipping rectangle (iv_DeltaScaleFilter): - final float scaleY = 1.0f / myy; + final double scaleY = 1.0d / myy; clipRect[0] *= scaleY; clipRect[1] *= scaleY; if (clipRect[1] < clipRect[0]) { - float tmp = clipRect[0]; + double tmp = clipRect[0]; clipRect[0] = clipRect[1]; clipRect[1] = tmp; } - final float scaleX = 1.0f / mxx; + final double scaleX = 1.0d / mxx; clipRect[2] *= scaleX; clipRect[3] *= scaleX; if (clipRect[3] < clipRect[2]) { - float tmp = clipRect[2]; + double tmp = clipRect[2]; clipRect[2] = clipRect[3]; clipRect[3] = tmp; } @@ -163,22 +162,22 @@ private static float adjustClipScale(final float[] clipRect, MarlinUtils.logInfo("clipRect (ClipScale): " + Arrays.toString(clipRect)); } - return 0.5f * (Math.abs(scaleX) + Math.abs(scaleY)); + return 0.5d * (Math.abs(scaleX) + Math.abs(scaleY)); } - private static float adjustClipInverseDelta(final float[] clipRect, - final float mxx, final float mxy, - final float myx, final float myy) + private static double adjustClipInverseDelta(final double[] clipRect, + final double mxx, final double mxy, + final double myx, final double myy) { // Adjust the clipping rectangle (iv_DeltaTransformFilter): - final float det = mxx * myy - mxy * myx; - final float imxx = myy / det; - final float imxy = -mxy / det; - final float imyx = -myx / det; - final float imyy = mxx / det; - - float xmin, xmax, ymin, ymax; - float x, y; + final double det = mxx * myy - mxy * myx; + final double imxx = myy / det; + final double imxy = -mxy / det; + final double imyx = -myx / det; + final double imyy = mxx / det; + + double xmin, xmax, ymin, ymax; + double x, y; // xmin, ymin: x = clipRect[2] * imxx + clipRect[0] * imxy; y = clipRect[2] * imyx + clipRect[0] * imyy; @@ -217,31 +216,31 @@ private static float adjustClipInverseDelta(final float[] clipRect, + Arrays.toString(clipRect)); } - final float scaleX = (float) Math.sqrt(imxx * imxx + imxy * imxy); - final float scaleY = (float) Math.sqrt(imyx * imyx + imyy * imyy); + final double scaleX = Math.sqrt(imxx * imxx + imxy * imxy); + final double scaleY = Math.sqrt(imyx * imyx + imyy * imyy); - return 0.5f * (scaleX + scaleY); + return 0.5d * (scaleX + scaleY); } - PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out, + DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, AffineTransform at) { if (at == null) { return out; } - float mxx = (float) at.getScaleX(); - float mxy = (float) at.getShearX(); - float myx = (float) at.getShearY(); - float myy = (float) at.getScaleY(); + double mxx = at.getScaleX(); + double mxy = at.getShearX(); + double myx = at.getShearY(); + double myy = at.getScaleY(); - if (mxy == 0.0f && myx == 0.0f) { - if (mxx == 1.0f && myy == 1.0f) { + if (mxy == 0.0d && myx == 0.0d) { + if (mxx == 1.0d && myy == 1.0d) { return out; } else { - return iv_DeltaScaleFilter.init(out, 1.0f / mxx, 1.0f / myy); + return iv_DeltaScaleFilter.init(out, 1.0d / mxx, 1.0d / myy); } } else { - final float det = mxx * myy - mxy * myx; + final double det = mxx * myy - mxy * myx; return iv_DeltaTransformFilter.init(out, myy / det, -mxy / det, @@ -250,14 +249,14 @@ PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out, } } - static final class DeltaScaleFilter implements PathConsumer2D { - private PathConsumer2D out; - private float sx, sy; + static final class DeltaScaleFilter implements DPathConsumer2D { + private DPathConsumer2D out; + private double sx, sy; DeltaScaleFilter() {} - DeltaScaleFilter init(PathConsumer2D out, - float mxx, float myy) + DeltaScaleFilter init(DPathConsumer2D out, + double mxx, double myy) { if (this.out != out) { this.out = out; @@ -268,27 +267,27 @@ DeltaScaleFilter init(PathConsumer2D out, } @Override - public void moveTo(float x0, float y0) { + public void moveTo(double x0, double y0) { out.moveTo(x0 * sx, y0 * sy); } @Override - public void lineTo(float x1, float y1) { + public void lineTo(double x1, double y1) { out.lineTo(x1 * sx, y1 * sy); } @Override - public void quadTo(float x1, float y1, - float x2, float y2) + public void quadTo(double x1, double y1, + double x2, double y2) { out.quadTo(x1 * sx, y1 * sy, x2 * sx, y2 * sy); } @Override - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) { out.curveTo(x1 * sx, y1 * sy, x2 * sx, y2 * sy, @@ -311,15 +310,15 @@ public long getNativeConsumer() { } } - static final class DeltaTransformFilter implements PathConsumer2D { - private PathConsumer2D out; - private float mxx, mxy, myx, myy; + static final class DeltaTransformFilter implements DPathConsumer2D { + private DPathConsumer2D out; + private double mxx, mxy, myx, myy; DeltaTransformFilter() {} - DeltaTransformFilter init(PathConsumer2D out, - float mxx, float mxy, - float myx, float myy) + DeltaTransformFilter init(DPathConsumer2D out, + double mxx, double mxy, + double myx, double myy) { if (this.out != out) { this.out = out; @@ -332,20 +331,20 @@ DeltaTransformFilter init(PathConsumer2D out, } @Override - public void moveTo(float x0, float y0) { + public void moveTo(double x0, double y0) { out.moveTo(x0 * mxx + y0 * mxy, x0 * myx + y0 * myy); } @Override - public void lineTo(float x1, float y1) { + public void lineTo(double x1, double y1) { out.lineTo(x1 * mxx + y1 * mxy, x1 * myx + y1 * myy); } @Override - public void quadTo(float x1, float y1, - float x2, float y2) + public void quadTo(double x1, double y1, + double x2, double y2) { out.quadTo(x1 * mxx + y1 * mxy, x1 * myx + y1 * myy, @@ -354,9 +353,9 @@ public void quadTo(float x1, float y1, } @Override - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) { out.curveTo(x1 * mxx + y1 * mxy, x1 * myx + y1 * myy, @@ -382,12 +381,12 @@ public long getNativeConsumer() { } } - static final class Path2DWrapper implements PathConsumer2D { - private Path2D.Float p2d; + static final class Path2DWrapper implements DPathConsumer2D { + private Path2D.Double p2d; Path2DWrapper() {} - Path2DWrapper init(Path2D.Float p2d) { + Path2DWrapper init(Path2D.Double p2d) { if (this.p2d != p2d) { this.p2d = p2d; } @@ -395,12 +394,12 @@ Path2DWrapper init(Path2D.Float p2d) { } @Override - public void moveTo(float x0, float y0) { + public void moveTo(double x0, double y0) { p2d.moveTo(x0, y0); } @Override - public void lineTo(float x1, float y1) { + public void lineTo(double x1, double y1) { p2d.lineTo(x1, y1); } @@ -413,15 +412,15 @@ public void closePath() { public void pathDone() {} @Override - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) { p2d.curveTo(x1, y1, x2, y2, x3, y3); } @Override - public void quadTo(float x1, float y1, float x2, float y2) { + public void quadTo(double x1, double y1, double x2, double y2) { p2d.quadTo(x1, y1, x2, y2); } @@ -431,12 +430,12 @@ public long getNativeConsumer() { } } - static final class ClosedPathDetector implements PathConsumer2D { + static final class ClosedPathDetector implements DPathConsumer2D { private final RendererContext rdrCtx; private final PolyStack stack; - private PathConsumer2D out; + private DPathConsumer2D out; ClosedPathDetector(final RendererContext rdrCtx) { this.rdrCtx = rdrCtx; @@ -450,7 +449,7 @@ static final class ClosedPathDetector implements PathConsumer2D { : new PolyStack(rdrCtx); } - ClosedPathDetector init(PathConsumer2D out) { + ClosedPathDetector init(DPathConsumer2D out) { if (this.out != out) { this.out = out; } @@ -484,7 +483,7 @@ public void closePath() { } @Override - public void moveTo(float x0, float y0) { + public void moveTo(double x0, double y0) { // previous path is not closed: finish(false); out.moveTo(x0, y0); @@ -496,20 +495,20 @@ private void finish(final boolean closed) { } @Override - public void lineTo(float x1, float y1) { + public void lineTo(double x1, double y1) { stack.pushLine(x1, y1); } @Override - public void curveTo(float x3, float y3, - float x2, float y2, - float x1, float y1) + public void curveTo(double x3, double y3, + double x2, double y2, + double x1, double y1) { stack.pushCubic(x1, y1, x2, y2, x3, y3); } @Override - public void quadTo(float x2, float y2, float x1, float y1) { + public void quadTo(double x2, double y2, double x1, double y1) { stack.pushQuad(x1, y1, x2, y2); } @@ -519,7 +518,7 @@ public long getNativeConsumer() { } } - static final class PathClipFilter implements PathConsumer2D { + static final class PathClipFilter implements DPathConsumer2D { private static final boolean TRACE = false; @@ -527,14 +526,14 @@ static final class PathClipFilter implements PathConsumer2D { private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad private static final int CLOSE = 2; - private PathConsumer2D out; + private DPathConsumer2D out; private int prev; // Bounds of the drawing region, at pixel precision. - private final float[] clipRect; + private final double[] clipRect; - private final float[] corners = new float[8]; + private final double[] corners = new double[8]; private boolean init_corners = false; private final IndexStack stack; @@ -551,13 +550,10 @@ static final class PathClipFilter implements PathConsumer2D { private boolean outside = false; // The starting point of the path - private float sx0, sy0; + private double sx0, sy0; - // The current point (TODO stupid repeated info) - private float cx0, cy0; - - // The current point OUTSIDE - private float cox0, coy0; + // The current point + private double cx0, cy0; private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER; private final CurveClipSplitter curveSplitter; @@ -574,7 +570,7 @@ static final class PathClipFilter implements PathConsumer2D { : new IndexStack(rdrCtx); } - PathClipFilter init(final PathConsumer2D out) { + PathClipFilter init(final DPathConsumer2D out) { if (this.out != out) { this.out = out; } @@ -600,14 +596,12 @@ void dispose() { } private void finishPath() { - if (outside) { - // criteria: inside or totally outside ? - if (gOutCode == 0) { - finish(); - } else { - this.outside = false; - stack.reset(); - } + // criteria: inside or totally outside ? + if (gOutCode == 0) { + finish(); + } else { + this.outside = false; + stack.reset(); } } @@ -618,8 +612,8 @@ private void finish() { if (init_corners) { init_corners = false; - final float[] _corners = corners; - final float[] _clipRect = clipRect; + final double[] _corners = corners; + final double[] _clipRect = clipRect; // Top Left (0): _corners[0] = _clipRect[2]; _corners[1] = _clipRect[0]; @@ -636,18 +630,16 @@ private void finish() { stack.pullAll(corners, out, (prev == MOVE_TO)); prev = DRAWING_OP_TO; } - // go to the last outside point: - this.cx0 = cox0; - this.cy0 = coy0; } @Override public void pathDone() { if (TRACE) { - System.out.println("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev); + MarlinUtils.logInfo("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev); } _closePath(); + // note: renderer's pathDone() must handle missing moveTo() if outside out.pathDone(); // this shouldn't matter since this object won't be used @@ -662,11 +654,13 @@ public void pathDone() { @Override public void closePath() { if (TRACE) { - System.out.println("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev); + MarlinUtils.logInfo("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev); } _closePath(); - out.closePath(); + if (prev == DRAWING_OP_TO) { + out.closePath(); + } // if outside, moveTo is needed if (sOutCode != 0) { @@ -684,27 +678,32 @@ public void closePath() { private void _closePath() { // preserve outside flag for the lineTo call below final boolean prevOutside = outside; - finishPath(); + if (prevOutside) { + finishPath(); + } if (prev == DRAWING_OP_TO) { // Should clip final int orCode = (cOutCode | sOutCode); if (orCode != 0) { - if (cx0 != sx0 || cy0 != sy0) { + if ((cx0 != sx0) || (cy0 != sy0)) { // restore outside flag before lineTo: this.outside = prevOutside; // may subdivide line: lineTo(sx0, sy0); + // finish if outside caused by lineTo: + if (outside) { + finishPath(); + } } } } - finishPath(); } @Override - public void moveTo(final float x0, final float y0) { + public void moveTo(final double x0, final double y0) { if (TRACE) { - System.out.println("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev); + MarlinUtils.logInfo("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev); } _closePath(); @@ -716,25 +715,21 @@ public void moveTo(final float x0, final float y0) { this.sOutCode = outcode; this.cx0 = x0; this.cy0 = y0; - this.sx0 = x0; this.sy0 = y0; } @Override - public void lineTo(final float xe, final float ye) { + public void lineTo(final double xe, final double ye) { final int outcode0 = this.cOutCode; final int outcode1 = Helpers.outcode(xe, ye, clipRect); if (TRACE) { if (subdivide) { - System.out.println("----------------------"); + MarlinUtils.logInfo("----------------------"); } - if (outside) { - System.out.println("LineTo co (" + cox0 + ", " + coy0 + ")"); - } - System.out.println("LineTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); + MarlinUtils.logInfo("LineTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); + MarlinUtils.logInfo("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); } // Should clip @@ -750,13 +745,8 @@ public void lineTo(final float xe, final float ye) { subdivide = false; boolean ret; // subdivide curve => callback with subdivided parts: - if (outside) { - ret = curveSplitter.splitLine(cox0, coy0, xe, ye, - orCode, this); - } else { - ret = curveSplitter.splitLine(cx0, cy0, xe, ye, - orCode, this); - } + ret = curveSplitter.splitLine(cx0, cy0, xe, ye, + orCode, this); // reentrance is done: subdivide = true; if (ret) { @@ -769,11 +759,11 @@ public void lineTo(final float xe, final float ye) { this.gOutCode &= sideCode; // keep last point coordinate before entering the clip again: this.outside = true; - this.cox0 = xe; - this.coy0 = ye; + this.cx0 = xe; + this.cy0 = ye; if (TRACE) { - System.out.println("skipped: (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("skipped: (" + cx0 + ", " + cy0 + ")"); } clip(sideCode, outcode0, outcode1); @@ -790,12 +780,12 @@ public void lineTo(final float xe, final float ye) { // emit last point outside before entering again... if (outcode0 != 0) { if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")"); } if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); + out.moveTo(cx0, cy0); } else { - out.lineTo(cox0, coy0); + out.lineTo(cx0, cy0); } prev = DRAWING_OP_TO; } @@ -811,7 +801,7 @@ public void lineTo(final float xe, final float ye) { this.cy0 = ye; if (TRACE && subdivide) { - System.out.println("----------------------"); + MarlinUtils.logInfo("----------------------"); } } @@ -853,9 +843,9 @@ private void clip(final int sideCode, } @Override - public void curveTo(final float x1, final float y1, - final float x2, final float y2, - final float xe, final float ye) + public void curveTo(final double x1, final double y1, + final double x2, final double y2, + final double xe, final double ye) { final int outcode0 = this.cOutCode; final int outcode1 = Helpers.outcode(x1, y1, clipRect); @@ -864,13 +854,10 @@ public void curveTo(final float x1, final float y1, if (TRACE) { if (subdivide) { - System.out.println("----------------------"); + MarlinUtils.logInfo("----------------------"); } - if (outside) { - System.out.println("CurveTo co (" + cox0 + ", " + coy0 + ")"); - } - System.out.println("CurveTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside); + MarlinUtils.logInfo("CurveTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); + MarlinUtils.logInfo("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside); } // Should clip @@ -886,15 +873,9 @@ public void curveTo(final float x1, final float y1, subdivide = false; // subdivide curve => callback with subdivided parts: boolean ret; - if (outside) { - ret = curveSplitter.splitCurve(cox0, coy0, x1, y1, - x2, y2, xe, ye, - orCode, this); - } else { - ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, - x2, y2, xe, ye, - orCode, this); - } + ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, + x2, y2, xe, ye, + orCode, this); // reentrance is done: subdivide = true; if (ret) { @@ -907,11 +888,11 @@ public void curveTo(final float x1, final float y1, this.gOutCode &= sideCode; // keep last point coordinate before entering the clip again: this.outside = true; - this.cox0 = xe; - this.coy0 = ye; + this.cx0 = xe; + this.cy0 = ye; if (TRACE) { - System.out.println("skipped: (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("skipped: (" + cx0 + ", " + cy0 + ")"); } clip(sideCode, outcode0, outcode3); @@ -928,12 +909,12 @@ public void curveTo(final float x1, final float y1, // emit last point outside before entering again... if (outcode0 != 0) { if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")"); } if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); + out.moveTo(cx0, cy0); } else { - out.lineTo(cox0, coy0); + out.lineTo(cx0, cy0); } prev = DRAWING_OP_TO; } @@ -949,13 +930,13 @@ public void curveTo(final float x1, final float y1, this.cy0 = ye; if (TRACE && subdivide) { - System.out.println("----------------------"); + MarlinUtils.logInfo("----------------------"); } } @Override - public void quadTo(final float x1, final float y1, - final float xe, final float ye) + public void quadTo(final double x1, final double y1, + final double xe, final double ye) { final int outcode0 = this.cOutCode; final int outcode1 = Helpers.outcode(x1, y1, clipRect); @@ -963,13 +944,10 @@ public void quadTo(final float x1, final float y1, if (TRACE) { if (subdivide) { - System.out.println("----------------------"); - } - if (outside) { - System.out.println("QuadTo co (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("----------------------"); } - System.out.println("QuadTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); - System.out.println("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); + MarlinUtils.logInfo("QuadTo c (" + cx0 + ", " + cy0 + ") outcode: " + outcode0); + MarlinUtils.logInfo("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside); } // Should clip @@ -985,13 +963,8 @@ public void quadTo(final float x1, final float y1, subdivide = false; // subdivide curve => callback with subdivided parts: boolean ret; - if (outside) { - ret = curveSplitter.splitQuad(cox0, coy0, x1, y1, - xe, ye, orCode, this); - } else { - ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, - xe, ye, orCode, this); - } + ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, + xe, ye, orCode, this); // reentrance is done: subdivide = true; if (ret) { @@ -1004,8 +977,8 @@ public void quadTo(final float x1, final float y1, this.gOutCode &= sideCode; // keep last point coordinate before entering the clip again: this.outside = true; - this.cox0 = xe; - this.coy0 = ye; + this.cx0 = xe; + this.cy0 = ye; clip(sideCode, outcode0, outcode2); return; @@ -1021,12 +994,12 @@ public void quadTo(final float x1, final float y1, // emit last point outside before entering again... if (outcode0 != 0) { if (TRACE) { - System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")"); + MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")"); } if (prev == MOVE_TO) { - out.moveTo(cox0, coy0); + out.moveTo(cx0, cy0); } else { - out.lineTo(cox0, coy0); + out.lineTo(cx0, cy0); } prev = DRAWING_OP_TO; } @@ -1042,7 +1015,7 @@ public void quadTo(final float x1, final float y1, this.cy0 = ye; if (TRACE && subdivide) { - System.out.println("----------------------"); + MarlinUtils.logInfo("----------------------"); } } @@ -1052,11 +1025,10 @@ public long getNativeConsumer() { } } - /* note: CurveClipSplitter uses double-precision for higher accuracy */ static final class CurveClipSplitter { - static final float LEN_TH = MarlinProperties.getSubdividerMinLength(); - static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0f); + static final double LEN_TH = MarlinProperties.getSubdividerMinLength(); + static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d); private static final boolean TRACE = false; @@ -1065,10 +1037,10 @@ static final class CurveClipSplitter { private final RendererContext rdrCtx; // scaled length threshold: - private float minLength; + private double minLength; // clip rectangle (ymin, ymax, xmin, xmax): - final float[] clipRect; + final double[] clipRect; // clip rectangle (ymin, ymax, xmin, xmax) including padding: final double[] clipRectPad = new double[4]; @@ -1081,19 +1053,19 @@ static final class CurveClipSplitter { private final double[] subdivTs = new double[MAX_N_CURVES]; // dirty curve - private final DCurve curve; + private final Curve curve; CurveClipSplitter(final RendererContext rdrCtx) { this.rdrCtx = rdrCtx; this.clipRect = rdrCtx.clipRect; - this.curve = /* rdrCtx.curve */ new DCurve(); // double-precision curve + this.curve = rdrCtx.curve; } void init() { this.init_clipRectPad = true; if (DO_CHECK_LENGTH) { - this.minLength = (this.rdrCtx.clipInvScale == 0.0f) ? LEN_TH + this.minLength = (this.rdrCtx.clipInvScale == 0.0d) ? LEN_TH : (LEN_TH * this.rdrCtx.clipInvScale); if (MarlinConst.DO_LOG_CLIP) { @@ -1107,7 +1079,7 @@ private void initPaddedClip() { // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY // adjust padded clip rectangle (ymin, ymax, xmin, xmax): // add a rounding error (curve subdivision ~ 0.1px): - final float[] _clipRect = clipRect; + final double[] _clipRect = clipRect; final double[] _clipRectPad = clipRectPad; _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING; @@ -1121,10 +1093,10 @@ private void initPaddedClip() { } } - boolean splitLine(final float x0, final float y0, - final float x1, final float y1, + boolean splitLine(final double x0, final double y0, + final double x1, final double y1, final int outCodeOR, - final PathConsumer2D out) + final DPathConsumer2D out) { if (TRACE) { MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")"); @@ -1141,11 +1113,11 @@ boolean splitLine(final float x0, final float y0, return subdivideAtIntersections(4, outCodeOR, out); } - boolean splitQuad(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, + boolean splitQuad(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, final int outCodeOR, - final PathConsumer2D out) + final DPathConsumer2D out) { if (TRACE) { MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")"); @@ -1163,12 +1135,12 @@ boolean splitQuad(final float x0, final float y0, return subdivideAtIntersections(6, outCodeOR, out); } - boolean splitCurve(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3, + boolean splitCurve(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3, final int outCodeOR, - final PathConsumer2D out) + final DPathConsumer2D out) { if (TRACE) { MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")"); @@ -1188,7 +1160,7 @@ boolean splitCurve(final float x0, final float y0, } private boolean subdivideAtIntersections(final int type, final int outCodeOR, - final PathConsumer2D out) + final DPathConsumer2D out) { final double[] mid = middle; final double[] subTs = subdivTs; @@ -1198,7 +1170,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR, initPaddedClip(); } - final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type, + final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type, outCodeOR, clipRectPad); if (TRACE) { @@ -1214,7 +1186,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR, for (int i = 0, off = 0; i < nSplits; i++, off += type) { final double t = subTs[i]; - DHelpers.subdivideAt((t - prevT) / (1.0d - prevT), + Helpers.subdivideAt((t - prevT) / (1.0d - prevT), mid, off, mid, off, type); prevT = t; } @@ -1229,18 +1201,18 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR, } static void emitCurrent(final int type, final double[] pts, - final int off, final PathConsumer2D out) + final int off, final DPathConsumer2D out) { // if instead of switch (perf + most probable cases first) if (type == 8) { - out.curveTo((float)pts[off + 2], (float)pts[off + 3], - (float)pts[off + 4], (float)pts[off + 5], - (float)pts[off + 6], (float)pts[off + 7]); + out.curveTo(pts[off + 2], pts[off + 3], + pts[off + 4], pts[off + 5], + pts[off + 6], pts[off + 7]); } else if (type == 4) { - out.lineTo((float)pts[off + 2], (float)pts[off + 3]); + out.lineTo(pts[off + 2], pts[off + 3]); } else { - out.quadTo((float)pts[off + 2], (float)pts[off + 3], - (float)pts[off + 4], (float)pts[off + 5]); + out.quadTo(pts[off + 2], pts[off + 3], + pts[off + 4], pts[off + 5]); } } } @@ -1250,16 +1222,16 @@ static final class CurveBasicMonotonizer { private static final int MAX_N_CURVES = 11; // squared half line width (for stroker) - private float lw2; + private double lw2; // number of splitted curves int nbSplits; // This is where the curve to be processed is put. We give it // enough room to store all curves. - final float[] middle = new float[MAX_N_CURVES * 6 + 2]; + final double[] middle = new double[MAX_N_CURVES * 6 + 2]; // t values at subdivision points - private final float[] subdivTs = new float[MAX_N_CURVES - 1]; + private final double[] subdivTs = new double[MAX_N_CURVES - 1]; // dirty curve private final Curve curve; @@ -1268,29 +1240,29 @@ static final class CurveBasicMonotonizer { this.curve = rdrCtx.curve; } - void init(final float lineWidth) { - this.lw2 = (lineWidth * lineWidth) / 4.0f; + void init(final double lineWidth) { + this.lw2 = (lineWidth * lineWidth) / 4.0d; } - CurveBasicMonotonizer curve(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3) + CurveBasicMonotonizer curve(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2, + final double x3, final double y3) { - final float[] mid = middle; + final double[] mid = middle; mid[0] = x0; mid[1] = y0; mid[2] = x1; mid[3] = y1; mid[4] = x2; mid[5] = y2; mid[6] = x3; mid[7] = y3; - final float[] subTs = subdivTs; + final double[] subTs = subdivTs; final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 8, lw2); - float prevT = 0.0f; + double prevT = 0.0d; for (int i = 0, off = 0; i < nSplits; i++, off += 6) { - final float t = subTs[i]; + final double t = subTs[i]; - Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT), + Helpers.subdivideCubicAt((t - prevT) / (1.0d - prevT), mid, off, mid, off, off + 6); prevT = t; } @@ -1299,22 +1271,22 @@ CurveBasicMonotonizer curve(final float x0, final float y0, return this; } - CurveBasicMonotonizer quad(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2) + CurveBasicMonotonizer quad(final double x0, final double y0, + final double x1, final double y1, + final double x2, final double y2) { - final float[] mid = middle; + final double[] mid = middle; mid[0] = x0; mid[1] = y0; mid[2] = x1; mid[3] = y1; mid[4] = x2; mid[5] = y2; - final float[] subTs = subdivTs; + final double[] subTs = subdivTs; final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 6, lw2); - float prevt = 0.0f; + double prevt = 0.0d; for (int i = 0, off = 0; i < nSplits; i++, off += 4) { - final float t = subTs[i]; - Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt), + final double t = subTs[i]; + Helpers.subdivideQuadAt((t - prevt) / (1.0d - prevt), mid, off, mid, off, off + 4); prevt = t; } @@ -1324,15 +1296,15 @@ CurveBasicMonotonizer quad(final float x0, final float y0, } } - static final class PathTracer implements PathConsumer2D { + static final class PathTracer implements DPathConsumer2D { private final String prefix; - private PathConsumer2D out; + private DPathConsumer2D out; PathTracer(String name) { this.prefix = name + ": "; } - PathTracer init(PathConsumer2D out) { + PathTracer init(DPathConsumer2D out) { if (this.out != out) { this.out = out; } @@ -1340,29 +1312,29 @@ PathTracer init(PathConsumer2D out) { } @Override - public void moveTo(float x0, float y0) { + public void moveTo(double x0, double y0) { log("p.moveTo(" + x0 + ", " + y0 + ");"); out.moveTo(x0, y0); } @Override - public void lineTo(float x1, float y1) { + public void lineTo(double x1, double y1) { log("p.lineTo(" + x1 + ", " + y1 + ");"); out.lineTo(x1, y1); } @Override - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) { log("p.curveTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ", " + x3 + ", " + y3 + ");"); out.curveTo(x1, y1, x2, y2, x3, y3); } @Override - public void quadTo(float x1, float y1, - float x2, float y2) { + public void quadTo(double x1, double y1, + double x2, double y2) { log("p.quadTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ");"); out.quadTo(x1, y1, x2, y2); } diff --git a/src/main/java/test/ClipShapeTest.java b/src/main/java/test/ClipShapeTest.java index 23bdcb2..e931ea4 100644 --- a/src/main/java/test/ClipShapeTest.java +++ b/src/main/java/test/ClipShapeTest.java @@ -1,7 +1,7 @@ package test; /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,10 +63,6 @@ * for paths made of either 9 lines, 4 quads, 2 cubics (random) * Note: Use the argument -slow to run more intensive tests (too much time) * - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic @@ -135,7 +131,6 @@ static enum ShapeMode { static final File OUTPUT_DIR = new File("."); static final AtomicBoolean isMarlin = new AtomicBoolean(); - static final AtomicBoolean isMarlinFloat = new AtomicBoolean(); static final AtomicBoolean isClipRuntime = new AtomicBoolean(); static { @@ -152,8 +147,7 @@ public void publish(LogRecord record) { if (msg != null) { // last space to avoid matching other settings: if (msg.startsWith("sun.java2d.renderer ")) { - isMarlin.set(msg.contains("MarlinRenderingEngine")); - isMarlinFloat.set(!msg.contains("DMarlinRenderingEngine")); + isMarlin.set(msg.contains("DMarlinRenderingEngine")); } if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) { isClipRuntime.set(msg.contains("true")); @@ -357,10 +351,7 @@ public static void main(String[] args) { NbPixels [All Test setups][n: 30] sum: 232 avg: 7.733 [1 | 27] */ THRESHOLD_DELTA = 2; - THRESHOLD_NBPIX = (USE_DASHES) ? - // float variant have higher uncertainty - ((isMarlinFloat.get()) ? 30 : 6) // low for double - : (isMarlinFloat.get()) ? 10 : 0; + THRESHOLD_NBPIX = (USE_DASHES) ? 6 : 0; } // Visual inspection (low threshold): diff --git a/src/main/java/test/EndlessLoop.java b/src/main/java/test/EndlessLoop.java index 2812921..acd1e94 100644 --- a/src/main/java/test/EndlessLoop.java +++ b/src/main/java/test/EndlessLoop.java @@ -18,12 +18,6 @@ drawLine(1.0E8) [AA=true]: 1.86741 ms. drawLine(1.0E8) [AA=true]: 4.550681 ms. drawLine(1.0E8) [AA=true]: 1.9914479999999999 ms. - * - AA org.marlin.pisces.MarlinRenderingEngine: -drawLine(1.0E8) [AA=true]: 50.357248999999996 ms. -drawLine(1.0E8) [AA=true]: 1.935198 ms. - -drawLine(7.0E15) [AA=true]: 48.779185999999996 ms. -drawLine(7.0E15) [AA=true]: 1.946686 ms. */ public class EndlessLoop extends JFrame { diff --git a/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine b/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine index e760a02..2530788 100644 --- a/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine +++ b/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine @@ -1,3 +1,2 @@ # Marlin Rendering Engine module -sun.java2d.marlin.MarlinRenderingEngine sun.java2d.marlin.DMarlinRenderingEngine diff --git a/src/main/resources/org/marlin/pisces/Version.properties b/src/main/resources/sun/java2d/marlin/Version.properties similarity index 100% rename from src/main/resources/org/marlin/pisces/Version.properties rename to src/main/resources/sun/java2d/marlin/Version.properties diff --git a/src/test/java/ClipShapeTest.java b/src/test/java/ClipShapeTest.java index 057bdde..c678590 100644 --- a/src/test/java/ClipShapeTest.java +++ b/src/test/java/ClipShapeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,10 +61,6 @@ * for paths made of either 9 lines, 4 quads, 2 cubics (random) * Note: Use the argument -slow to run more intensive tests (too much time) * - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic - * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic @@ -133,7 +129,6 @@ static enum ShapeMode { static final File OUTPUT_DIR = new File("."); static final AtomicBoolean isMarlin = new AtomicBoolean(); - static final AtomicBoolean isMarlinFloat = new AtomicBoolean(); static final AtomicBoolean isClipRuntime = new AtomicBoolean(); static { @@ -150,8 +145,7 @@ public void publish(LogRecord record) { if (msg != null) { // last space to avoid matching other settings: if (msg.startsWith("sun.java2d.renderer ")) { - isMarlin.set(msg.contains("MarlinRenderingEngine")); - isMarlinFloat.set(!msg.contains("DMarlinRenderingEngine")); + isMarlin.set(msg.contains("DMarlinRenderingEngine")); } if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) { isClipRuntime.set(msg.contains("true")); @@ -355,10 +349,7 @@ public static void main(String[] args) { NbPixels [All Test setups][n: 30] sum: 232 avg: 7.733 [1 | 27] */ THRESHOLD_DELTA = 2; - THRESHOLD_NBPIX = (USE_DASHES) ? - // float variant have higher uncertainty - ((isMarlinFloat.get()) ? 30 : 6) // low for double - : (isMarlinFloat.get()) ? 10 : 0; + THRESHOLD_NBPIX = (USE_DASHES) ? 6 : 0; } // Visual inspection (low threshold):