--- /dev/null 2015-11-20 13:32:50.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/java2d/marlin/CollinearSimplifier.java 2015-11-20 13:32:50.000000000 -0800 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015, 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 sun.awt.geom.PathConsumer2D; + +final class CollinearSimplifier implements PathConsumer2D { + + enum SimplifierState { + + Empty, PreviousPoint, PreviousLine + }; + // slope precision threshold + static final float EPS = 1e-4f; // aaime proposed 1e-3f + + PathConsumer2D delegate; + SimplifierState state; + float px1, py1, px2, py2; + float pslope; + + CollinearSimplifier() { + } + + public CollinearSimplifier init(PathConsumer2D delegate) { + this.delegate = delegate; + this.state = SimplifierState.Empty; + + return this; // fluent API + } + + @Override + public void pathDone() { + emitStashedLine(); + state = SimplifierState.Empty; + delegate.pathDone(); + } + + @Override + public void closePath() { + emitStashedLine(); + state = SimplifierState.Empty; + delegate.closePath(); + } + + @Override + public long getNativeConsumer() { + return 0; + } + + @Override + public void quadTo(float x1, float y1, float x2, float y2) { + emitStashedLine(); + delegate.quadTo(x1, y1, x2, y2); + // final end point: + state = SimplifierState.PreviousPoint; + px1 = x2; + py1 = y2; + } + + @Override + public void curveTo(float x1, float y1, float x2, float y2, + float x3, float y3) { + emitStashedLine(); + delegate.curveTo(x1, y1, x2, y2, x3, y3); + // final end point: + state = SimplifierState.PreviousPoint; + px1 = x3; + py1 = y3; + } + + @Override + public void moveTo(float x, float y) { + emitStashedLine(); + delegate.moveTo(x, y); + state = SimplifierState.PreviousPoint; + px1 = x; + py1 = y; + } + + @Override + public void lineTo(final float x, final float y) { + switch (state) { + case Empty: + delegate.lineTo(x, y); + state = SimplifierState.PreviousPoint; + px1 = x; + py1 = y; + return; + + case PreviousPoint: + state = SimplifierState.PreviousLine; + px2 = x; + py2 = y; + pslope = getSlope(px1, py1, x, y); + return; + + case PreviousLine: + final float slope = getSlope(px2, py2, x, y); + // test for collinearity + if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) { + // merge segments + px2 = x; + py2 = y; + return; + } + // emit previous segment + delegate.lineTo(px2, py2); + px1 = px2; + py1 = py2; + px2 = x; + py2 = y; + pslope = slope; + return; + default: + } + } + + private void emitStashedLine() { + if (state == SimplifierState.PreviousLine) { + delegate.lineTo(px2, py2); + } + } + + private static float getSlope(float x1, float y1, float x2, float y2) { + float dy = y2 - y1; + if (dy == 0f) { + return (x2 > x1) ? Float.POSITIVE_INFINITY + : Float.NEGATIVE_INFINITY; + } + return (x2 - x1) / dy; + } +}