1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d.marlin; 27 28 import sun.awt.geom.PathConsumer2D; 29 30 final class CollinearSimplifier implements PathConsumer2D { 31 32 enum SimplifierState { 33 34 Empty, PreviousPoint, PreviousLine 35 }; 36 // slope precision threshold 37 static final float EPS = 1e-4f; // aaime proposed 1e-3f 38 39 PathConsumer2D delegate; 40 SimplifierState state; 41 float px1, py1, px2, py2; 42 float pslope; 43 44 CollinearSimplifier() { 45 } 46 47 public CollinearSimplifier init(PathConsumer2D delegate) { 48 this.delegate = delegate; 49 this.state = SimplifierState.Empty; 50 51 return this; // fluent API 52 } 53 54 @Override 55 public void pathDone() { 56 emitStashedLine(); 57 state = SimplifierState.Empty; 58 delegate.pathDone(); 59 } 60 61 @Override 62 public void closePath() { 63 emitStashedLine(); 64 state = SimplifierState.Empty; 65 delegate.closePath(); 66 } 67 68 @Override 69 public long getNativeConsumer() { 70 return 0; 71 } 72 73 @Override 74 public void quadTo(float x1, float y1, float x2, float y2) { 75 emitStashedLine(); 76 delegate.quadTo(x1, y1, x2, y2); 77 // final end point: 78 state = SimplifierState.PreviousPoint; 79 px1 = x2; 80 py1 = y2; 81 } 82 83 @Override 84 public void curveTo(float x1, float y1, float x2, float y2, 85 float x3, float y3) { 86 emitStashedLine(); 87 delegate.curveTo(x1, y1, x2, y2, x3, y3); 88 // final end point: 89 state = SimplifierState.PreviousPoint; 90 px1 = x3; 91 py1 = y3; 92 } 93 94 @Override 95 public void moveTo(float x, float y) { 96 emitStashedLine(); 97 delegate.moveTo(x, y); 98 state = SimplifierState.PreviousPoint; 99 px1 = x; 100 py1 = y; 101 } 102 103 @Override 104 public void lineTo(final float x, final float y) { 105 switch (state) { 106 case Empty: 107 delegate.lineTo(x, y); 108 state = SimplifierState.PreviousPoint; 109 px1 = x; 110 py1 = y; 111 return; 112 113 case PreviousPoint: 114 state = SimplifierState.PreviousLine; 115 px2 = x; 116 py2 = y; 117 pslope = getSlope(px1, py1, x, y); 118 return; 119 120 case PreviousLine: 121 final float slope = getSlope(px2, py2, x, y); 122 // test for collinearity 123 if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) { 124 // merge segments 125 px2 = x; 126 py2 = y; 127 return; 128 } 129 // emit previous segment 130 delegate.lineTo(px2, py2); 131 px1 = px2; 132 py1 = py2; 133 px2 = x; 134 py2 = y; 135 pslope = slope; 136 return; 137 default: 138 } 139 } 140 141 private void emitStashedLine() { 142 if (state == SimplifierState.PreviousLine) { 143 delegate.lineTo(px2, py2); 144 } 145 } 146 147 private static float getSlope(float x1, float y1, float x2, float y2) { 148 float dy = y2 - y1; 149 if (dy == 0f) { 150 return (x2 > x1) ? Float.POSITIVE_INFINITY 151 : Float.NEGATIVE_INFINITY; 152 } 153 return (x2 - x1) / dy; 154 } 155 }