1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 22package java.awt.geom; 23 24import java.util.NoSuchElementException; 25 26import org.apache.harmony.awt.internal.nls.Messages; 27 28/** 29 * The Class FlatteningPathIterator takes a PathIterator for traversing a curved 30 * shape and flattens it by estimating the curve as a series of line segments. 31 * The flattening factor indicates how far the estimating line segments are 32 * allowed to be from the actual curve: the FlatteningPathIterator will keep 33 * dividing each curved segment into smaller and smaller flat segments until 34 * either the segments are within the flattening factor of the curve or until 35 * the buffer limit is reached. 36 * 37 * @since Android 1.0 38 */ 39public class FlatteningPathIterator implements PathIterator { 40 41 /** 42 * The default points buffer size. 43 */ 44 private static final int BUFFER_SIZE = 16; 45 46 /** 47 * The default curve subdivision limit. 48 */ 49 private static final int BUFFER_LIMIT = 16; 50 51 /** 52 * The points buffer capacity. 53 */ 54 private static final int BUFFER_CAPACITY = 16; 55 56 /** 57 * The type of current segment to be flat. 58 */ 59 int bufType; 60 61 /** 62 * The curve subdivision limit. 63 */ 64 int bufLimit; 65 66 /** 67 * The current points buffer size. 68 */ 69 int bufSize; 70 71 /** 72 * The inner cursor position in points buffer. 73 */ 74 int bufIndex; 75 76 /** 77 * The current subdivision count. 78 */ 79 int bufSubdiv; 80 81 /** 82 * The points buffer. 83 */ 84 double buf[]; 85 86 /** 87 * The indicator of empty points buffer. 88 */ 89 boolean bufEmpty = true; 90 91 /** 92 * The source PathIterator. 93 */ 94 PathIterator p; 95 96 /** 97 * The flatness of new path. 98 */ 99 double flatness; 100 101 /** 102 * The square of flatness. 103 */ 104 double flatness2; 105 106 /** 107 * The x coordinate of previous path segment. 108 */ 109 double px; 110 111 /** 112 * The y coordinate of previous path segment. 113 */ 114 double py; 115 116 /** 117 * The temporary buffer for getting points from PathIterator. 118 */ 119 double coords[] = new double[6]; 120 121 /** 122 * Instantiates a new flattening path iterator given the path iterator for a 123 * (possibly) curved path and a flattening factor which indicates how close 124 * together the points on the curve should be chosen. The buffer limit 125 * defaults to 16 which means that each curve will be divided into no more 126 * than 16 segments regardless of the flattening factor. 127 * 128 * @param path 129 * the path iterator of the original curve. 130 * @param flatness 131 * the flattening factor that indicates how far the flat path is 132 * allowed to be from the actual curve in order to decide when to 133 * stop dividing the path into smaller and smaller segments. 134 * @throws IllegalArgumentException 135 * if the flatness is less than zero. 136 * @throws NullPointerException 137 * if the path is null. 138 */ 139 public FlatteningPathIterator(PathIterator path, double flatness) { 140 this(path, flatness, BUFFER_LIMIT); 141 } 142 143 /** 144 * Instantiates a new flattening path iterator given the path iterator for a 145 * (possibly) curved path and a flattening factor and a buffer limit. The 146 * FlatteningPathIterator will keep dividing each curved segment into 147 * smaller and smaller flat segments until either the segments are within 148 * the flattening factor of the curve or until the buffer limit is reached. 149 * 150 * @param path 151 * the path iterator of the original curve. 152 * @param flatness 153 * the flattening factor that indicates how far the flat path is 154 * allowed to be from the actual curve in order to decide when to 155 * stop dividing the path into smaller and smaller segments. 156 * @param limit 157 * the maximum number of flat segments to divide each curve into. 158 * @throws IllegalArgumentException 159 * if the flatness or limit is less than zero. 160 * @throws NullPointerException 161 * if the path is null. 162 */ 163 public FlatteningPathIterator(PathIterator path, double flatness, int limit) { 164 if (flatness < 0.0) { 165 // awt.206=Flatness is less then zero 166 throw new IllegalArgumentException(Messages.getString("awt.206")); //$NON-NLS-1$ 167 } 168 if (limit < 0) { 169 // awt.207=Limit is less then zero 170 throw new IllegalArgumentException(Messages.getString("awt.207")); //$NON-NLS-1$ 171 } 172 if (path == null) { 173 // awt.208=Path is null 174 throw new NullPointerException(Messages.getString("awt.208")); //$NON-NLS-1$ 175 } 176 this.p = path; 177 this.flatness = flatness; 178 this.flatness2 = flatness * flatness; 179 this.bufLimit = limit; 180 this.bufSize = Math.min(bufLimit, BUFFER_SIZE); 181 this.buf = new double[bufSize]; 182 this.bufIndex = bufSize; 183 } 184 185 /** 186 * Gets the flattening factor. 187 * 188 * @return the flattening factor. 189 */ 190 public double getFlatness() { 191 return flatness; 192 } 193 194 /** 195 * Gets the maximum number of subdivisions per curved segment. 196 * 197 * @return the maximum number of subdivisions per curved segment. 198 */ 199 public int getRecursionLimit() { 200 return bufLimit; 201 } 202 203 public int getWindingRule() { 204 return p.getWindingRule(); 205 } 206 207 public boolean isDone() { 208 return bufEmpty && p.isDone(); 209 } 210 211 /** 212 * Calculates flat path points for current segment of the source shape. Line 213 * segment is flat by itself. Flatness of quad and cubic curves evaluated by 214 * getFlatnessSq() method. Curves subdivided until current flatness is 215 * bigger than user defined and subdivision limit isn't exhausted. Single 216 * source segment translated to series of buffer points. The less flatness 217 * the bigger series. Every currentSegment() call extract one point from the 218 * buffer. When series completed evaluate() takes next source shape segment. 219 */ 220 void evaluate() { 221 if (bufEmpty) { 222 bufType = p.currentSegment(coords); 223 } 224 225 switch (bufType) { 226 case SEG_MOVETO: 227 case SEG_LINETO: 228 px = coords[0]; 229 py = coords[1]; 230 break; 231 case SEG_QUADTO: 232 if (bufEmpty) { 233 bufIndex -= 6; 234 buf[bufIndex + 0] = px; 235 buf[bufIndex + 1] = py; 236 System.arraycopy(coords, 0, buf, bufIndex + 2, 4); 237 bufSubdiv = 0; 238 } 239 240 while (bufSubdiv < bufLimit) { 241 if (QuadCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) { 242 break; 243 } 244 245 // Realloc buffer 246 if (bufIndex <= 4) { 247 double tmp[] = new double[bufSize + BUFFER_CAPACITY]; 248 System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize 249 - bufIndex); 250 buf = tmp; 251 bufSize += BUFFER_CAPACITY; 252 bufIndex += BUFFER_CAPACITY; 253 } 254 255 QuadCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex); 256 257 bufIndex -= 4; 258 bufSubdiv++; 259 } 260 261 bufIndex += 4; 262 px = buf[bufIndex]; 263 py = buf[bufIndex + 1]; 264 265 bufEmpty = (bufIndex == bufSize - 2); 266 if (bufEmpty) { 267 bufIndex = bufSize; 268 bufType = SEG_LINETO; 269 } else { 270 bufSubdiv--; 271 } 272 break; 273 case SEG_CUBICTO: 274 if (bufEmpty) { 275 bufIndex -= 8; 276 buf[bufIndex + 0] = px; 277 buf[bufIndex + 1] = py; 278 System.arraycopy(coords, 0, buf, bufIndex + 2, 6); 279 bufSubdiv = 0; 280 } 281 282 while (bufSubdiv < bufLimit) { 283 if (CubicCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) { 284 break; 285 } 286 287 // Realloc buffer 288 if (bufIndex <= 6) { 289 double tmp[] = new double[bufSize + BUFFER_CAPACITY]; 290 System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize 291 - bufIndex); 292 buf = tmp; 293 bufSize += BUFFER_CAPACITY; 294 bufIndex += BUFFER_CAPACITY; 295 } 296 297 CubicCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex); 298 299 bufIndex -= 6; 300 bufSubdiv++; 301 } 302 303 bufIndex += 6; 304 px = buf[bufIndex]; 305 py = buf[bufIndex + 1]; 306 307 bufEmpty = (bufIndex == bufSize - 2); 308 if (bufEmpty) { 309 bufIndex = bufSize; 310 bufType = SEG_LINETO; 311 } else { 312 bufSubdiv--; 313 } 314 break; 315 } 316 317 } 318 319 public void next() { 320 if (bufEmpty) { 321 p.next(); 322 } 323 } 324 325 public int currentSegment(float[] coords) { 326 if (isDone()) { 327 // awt.4B=Iterator out of bounds 328 throw new NoSuchElementException(Messages.getString("awt.4Bx")); //$NON-NLS-1$ 329 } 330 evaluate(); 331 int type = bufType; 332 if (type != SEG_CLOSE) { 333 coords[0] = (float)px; 334 coords[1] = (float)py; 335 if (type != SEG_MOVETO) { 336 type = SEG_LINETO; 337 } 338 } 339 return type; 340 } 341 342 public int currentSegment(double[] coords) { 343 if (isDone()) { 344 // awt.4B=Iterator out of bounds 345 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ 346 } 347 evaluate(); 348 int type = bufType; 349 if (type != SEG_CLOSE) { 350 coords[0] = px; 351 coords[1] = py; 352 if (type != SEG_MOVETO) { 353 type = SEG_LINETO; 354 } 355 } 356 return type; 357 } 358} 359