1 /*
2 * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.javafx.geom;
27
28 import com.sun.javafx.geom.transform.BaseTransform;
29
30 /**
31 * The {@code Path2D} class provides a simple, yet flexible
32 * shape which represents an arbitrary geometric path.
33 * It can fully represent any path which can be iterated by the
34 * {@link PathIterator} interface including all of its segment
35 * types and winding rules and it implements all of the
36 * basic hit testing methods of the {@link Shape} interface.
37 * <p>
38 * Use {@link Path2D} when dealing with data that can be represented
39 * and used with floating point precision.
40 * <p>
41 * {@code Path2D} provides exactly those facilities required for
42 * basic construction and management of a geometric path and
43 * implementation of the above interfaces with little added
44 * interpretation.
45 * If it is useful to manipulate the interiors of closed
46 * geometric shapes beyond simple hit testing then the
47 * {@link Area} class provides additional capabilities
48 * specifically targeted at closed figures.
84 *
85 * @see PathIterator#WIND_NON_ZERO
86 */
87 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
88
89 // For code simplicity, copy these constants to our namespace
90 // and cast them to byte constants for easy storage.
91 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
92 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
93 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
94 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
95 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
96
97 byte[] pointTypes;
98 int numTypes;
99 int numCoords;
100 int windingRule;
101
102 static final int INIT_SIZE = 20;
103 static final int EXPAND_MAX = 500;
104
105 float floatCoords[];
106 float moveX, moveY;
107 float prevX, prevY;
108 float currX, currY;
109
110 /**
111 * Constructs a new empty single precision {@code Path2D} object
112 * with a default winding rule of {@link #WIND_NON_ZERO}.
113 */
114 public Path2D() {
115 this(WIND_NON_ZERO, INIT_SIZE);
116 }
117
118 /**
119 * Constructs a new empty single precision {@code Path2D} object
120 * with the specified winding rule to control operations that
121 * require the interior of the path to be defined.
122 *
123 * @param rule the winding rule
159 public Path2D(Shape s) {
160 this(s, null);
161 }
162
163 /**
164 * Constructs a new single precision {@code Path2D} object
165 * from an arbitrary {@link Shape} object, transformed by an
166 * {@link BaseTransform} object.
167 * All of the initial geometry and the winding rule for this path are
168 * taken from the specified {@code Shape} object and transformed
169 * by the specified {@code BaseTransform} object.
170 *
171 * @param s the specified {@code Shape} object
172 * @param tx the specified {@code BaseTransform} object
173 */
174 public Path2D(Shape s, BaseTransform tx) {
175 if (s instanceof Path2D) {
176 Path2D p2d = (Path2D) s;
177 setWindingRule(p2d.windingRule);
178 this.numTypes = p2d.numTypes;
179 //this.pointTypes = Arrays.copyOf(p2d.pointTypes,
180 // p2d.pointTypes.length); // jk16 dependency
181 this.pointTypes = copyOf(p2d.pointTypes,
182 p2d.pointTypes.length);
183 this.numCoords = p2d.numCoords;
184 if (tx == null || tx.isIdentity()) {
185 this.floatCoords = copyOf(p2d.floatCoords, numCoords);
186 this.moveX = p2d.moveX;
187 this.moveY = p2d.moveY;
188 this.prevX = p2d.prevX;
189 this.prevY = p2d.prevY;
190 this.currX = p2d.currX;
191 this.currY = p2d.currY;
192 } else {
193 this.floatCoords = new float[numCoords + 6];
194 tx.transform(p2d.floatCoords, 0, this.floatCoords, 0, numCoords / 2);
195 floatCoords[numCoords + 0] = moveX;
196 floatCoords[numCoords + 1] = moveY;
197 floatCoords[numCoords + 2] = prevX;
198 floatCoords[numCoords + 3] = prevY;
199 floatCoords[numCoords + 4] = currX;
200 floatCoords[numCoords + 5] = currY;
201 tx.transform(this.floatCoords, numCoords, this.floatCoords, numCoords, 3);
202 moveX = floatCoords[numCoords + 0];
203 moveY = floatCoords[numCoords + 1];
204 prevX = floatCoords[numCoords + 2];
205 prevY = floatCoords[numCoords + 3];
319 // (x1, y1) -> (x3, y3)
320 // We also need to deal with upside down and/or backwards rectangles
321 int x, y, w, h;
322 if (x2 < x0) { x = x2; w = x0 - x2; }
323 else { x = x0; w = x2 - x0; }
324 if (y2 < y0) { y = y2; h = y0 - y2; }
325 else { y = y0; h = y2 - y0; }
326 // Overflow protection...
327 if (w < 0) return false;
328 if (h < 0) return false;
329
330 if (retrect != null) {
331 retrect.setBounds(x, y, w, h);
332 }
333 return true;
334 }
335 return false;
336 }
337
338 void needRoom(boolean needMove, int newCoords) {
339 if (needMove && numTypes == 0) {
340 throw new IllegalPathStateException("missing initial moveto "+
341 "in path definition");
342 }
343 int size = pointTypes.length;
344 if (size == 0) {
345 pointTypes = new byte[2];
346 } else if (numTypes >= size) {
347 int grow = size;
348 if (grow > EXPAND_MAX) {
349 grow = EXPAND_MAX;
350 }
351 //pointTypes = Arrays.copyOf(pointTypes, size+grow); // jk16 dependency
352 pointTypes = copyOf(pointTypes, size+grow);
353 }
354 size = floatCoords.length;
355 if (numCoords + newCoords > size) {
356 int grow = size;
357 if (grow > EXPAND_MAX * 2) {
358 grow = EXPAND_MAX * 2;
359 }
360 if (grow < newCoords) {
361 grow = newCoords;
362 }
363 //floatCoords = Arrays.copyOf(floatCoords, size+grow); // jk16 dependency
364 floatCoords = copyOf(floatCoords, size+grow);
365 }
366 }
367
368 /**
369 * Adds a point to the path by moving to the specified
370 * coordinates specified in float precision.
371 *
372 * @param x the specified X coordinate
373 * @param y the specified Y coordinate
374 */
375 public final void moveTo(float x, float y) {
376 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
377 floatCoords[numCoords-2] = moveX = prevX = currX = x;
378 floatCoords[numCoords-1] = moveY = prevY = currY = y;
379 } else {
380 needRoom(false, 2);
381 pointTypes[numTypes++] = SEG_MOVETO;
382 floatCoords[numCoords++] = moveX = prevX = currX = x;
383 floatCoords[numCoords++] = moveY = prevY = currY = y;
384 }
2271 int typeIdx;
2272 int pointIdx;
2273 Path2D path;
2274
2275 Iterator(Path2D path) {
2276 this.path = path;
2277 }
2278
2279 public int getWindingRule() {
2280 return path.getWindingRule();
2281 }
2282
2283 public boolean isDone() {
2284 return (typeIdx >= path.numTypes);
2285 }
2286
2287 public void next() {
2288 int type = path.pointTypes[typeIdx++];
2289 pointIdx += curvecoords[type];
2290 }
2291 }
2292
2293 // jk16 dependency methods
2294 static byte[] copyOf(byte[] original, int newLength) {
2295 byte[] copy = new byte[newLength];
2296 System.arraycopy(original, 0, copy, 0,
2297 Math.min(original.length, newLength));
2298 return copy;
2299 }
2300 static float[] copyOf(float[] original, int newLength) {
2301 float[] copy = new float[newLength];
2302 System.arraycopy(original, 0, copy, 0,
2303 Math.min(original.length, newLength));
2304 return copy;
2305 }
2306
2307 public void setTo(Path2D otherPath) {
2308 numTypes = otherPath.numTypes;
2309 numCoords = otherPath.numCoords;
2310 if (numTypes > pointTypes.length) {
2311 pointTypes = new byte[numTypes];
2312 }
2313 System.arraycopy(otherPath.pointTypes, 0, pointTypes, 0, numTypes);
2314 if (numCoords > floatCoords.length) {
2315 floatCoords = new float[numCoords];
2316 }
2317 System.arraycopy(otherPath.floatCoords, 0, floatCoords, 0, numCoords);
2318 windingRule = otherPath.windingRule;
2319 moveX = otherPath.moveX;
2320 moveY = otherPath.moveY;
2321 prevX = otherPath.prevX;
2322 prevY = otherPath.prevY;
2323 currX = otherPath.currX;
2324 currY = otherPath.currY;
|
1 /*
2 * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.javafx.geom;
27
28 import com.sun.javafx.geom.transform.BaseTransform;
29 import java.util.Arrays;
30
31 /**
32 * The {@code Path2D} class provides a simple, yet flexible
33 * shape which represents an arbitrary geometric path.
34 * It can fully represent any path which can be iterated by the
35 * {@link PathIterator} interface including all of its segment
36 * types and winding rules and it implements all of the
37 * basic hit testing methods of the {@link Shape} interface.
38 * <p>
39 * Use {@link Path2D} when dealing with data that can be represented
40 * and used with floating point precision.
41 * <p>
42 * {@code Path2D} provides exactly those facilities required for
43 * basic construction and management of a geometric path and
44 * implementation of the above interfaces with little added
45 * interpretation.
46 * If it is useful to manipulate the interiors of closed
47 * geometric shapes beyond simple hit testing then the
48 * {@link Area} class provides additional capabilities
49 * specifically targeted at closed figures.
85 *
86 * @see PathIterator#WIND_NON_ZERO
87 */
88 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
89
90 // For code simplicity, copy these constants to our namespace
91 // and cast them to byte constants for easy storage.
92 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
93 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
94 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
95 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
96 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
97
98 byte[] pointTypes;
99 int numTypes;
100 int numCoords;
101 int windingRule;
102
103 static final int INIT_SIZE = 20;
104 static final int EXPAND_MAX = 500;
105 static final int EXPAND_MAX_COORDS = EXPAND_MAX * 2;
106
107 float floatCoords[];
108 float moveX, moveY;
109 float prevX, prevY;
110 float currX, currY;
111
112 /**
113 * Constructs a new empty single precision {@code Path2D} object
114 * with a default winding rule of {@link #WIND_NON_ZERO}.
115 */
116 public Path2D() {
117 this(WIND_NON_ZERO, INIT_SIZE);
118 }
119
120 /**
121 * Constructs a new empty single precision {@code Path2D} object
122 * with the specified winding rule to control operations that
123 * require the interior of the path to be defined.
124 *
125 * @param rule the winding rule
161 public Path2D(Shape s) {
162 this(s, null);
163 }
164
165 /**
166 * Constructs a new single precision {@code Path2D} object
167 * from an arbitrary {@link Shape} object, transformed by an
168 * {@link BaseTransform} object.
169 * All of the initial geometry and the winding rule for this path are
170 * taken from the specified {@code Shape} object and transformed
171 * by the specified {@code BaseTransform} object.
172 *
173 * @param s the specified {@code Shape} object
174 * @param tx the specified {@code BaseTransform} object
175 */
176 public Path2D(Shape s, BaseTransform tx) {
177 if (s instanceof Path2D) {
178 Path2D p2d = (Path2D) s;
179 setWindingRule(p2d.windingRule);
180 this.numTypes = p2d.numTypes;
181 this.pointTypes = Arrays.copyOf(p2d.pointTypes, numTypes);
182 this.numCoords = p2d.numCoords;
183 if (tx == null || tx.isIdentity()) {
184 this.floatCoords = Arrays.copyOf(p2d.floatCoords, numCoords);
185 this.moveX = p2d.moveX;
186 this.moveY = p2d.moveY;
187 this.prevX = p2d.prevX;
188 this.prevY = p2d.prevY;
189 this.currX = p2d.currX;
190 this.currY = p2d.currY;
191 } else {
192 this.floatCoords = new float[numCoords + 6];
193 tx.transform(p2d.floatCoords, 0, this.floatCoords, 0, numCoords / 2);
194 floatCoords[numCoords + 0] = moveX;
195 floatCoords[numCoords + 1] = moveY;
196 floatCoords[numCoords + 2] = prevX;
197 floatCoords[numCoords + 3] = prevY;
198 floatCoords[numCoords + 4] = currX;
199 floatCoords[numCoords + 5] = currY;
200 tx.transform(this.floatCoords, numCoords, this.floatCoords, numCoords, 3);
201 moveX = floatCoords[numCoords + 0];
202 moveY = floatCoords[numCoords + 1];
203 prevX = floatCoords[numCoords + 2];
204 prevY = floatCoords[numCoords + 3];
318 // (x1, y1) -> (x3, y3)
319 // We also need to deal with upside down and/or backwards rectangles
320 int x, y, w, h;
321 if (x2 < x0) { x = x2; w = x0 - x2; }
322 else { x = x0; w = x2 - x0; }
323 if (y2 < y0) { y = y2; h = y0 - y2; }
324 else { y = y0; h = y2 - y0; }
325 // Overflow protection...
326 if (w < 0) return false;
327 if (h < 0) return false;
328
329 if (retrect != null) {
330 retrect.setBounds(x, y, w, h);
331 }
332 return true;
333 }
334 return false;
335 }
336
337 void needRoom(boolean needMove, int newCoords) {
338 if (needMove && (numTypes == 0)) {
339 throw new IllegalPathStateException("missing initial moveto "+
340 "in path definition");
341 }
342 int size = pointTypes.length;
343 if (size == 0) {
344 pointTypes = new byte[2];
345 } else if (numTypes >= size) {
346 pointTypes = expandPointTypes(pointTypes, 1);
347 }
348 size = floatCoords.length;
349 if (numCoords > (floatCoords.length - newCoords)) {
350 floatCoords = expandCoords(floatCoords, newCoords);
351 }
352 }
353
354 static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
355 final int oldSize = oldPointTypes.length;
356 final int newSizeMin = oldSize + needed;
357 if (newSizeMin < oldSize) {
358 // hard overflow failure - we can't even accommodate
359 // new items without overflowing
360 throw new ArrayIndexOutOfBoundsException(
361 "pointTypes exceeds maximum capacity !");
362 }
363 // growth algorithm computation
364 int grow = oldSize;
365 if (grow > EXPAND_MAX) {
366 grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
367 } else if (grow < INIT_SIZE) {
368 grow = INIT_SIZE; // ensure > 6 (cubics)
369 }
370 assert grow > 0;
371
372 int newSize = oldSize + grow;
373 if (newSize < newSizeMin) {
374 // overflow in growth algorithm computation
375 newSize = Integer.MAX_VALUE;
376 }
377
378 while (true) {
379 try {
380 // try allocating the larger array
381 return Arrays.copyOf(oldPointTypes, newSize);
382 } catch (OutOfMemoryError oome) {
383 if (newSize == newSizeMin) {
384 throw oome;
385 }
386 }
387 newSize = newSizeMin + (newSize - newSizeMin) / 2;
388 }
389 }
390
391 static float[] expandCoords(float[] oldCoords, int needed) {
392 final int oldSize = oldCoords.length;
393 final int newSizeMin = oldSize + needed;
394 if (newSizeMin < oldSize) {
395 // hard overflow failure - we can't even accommodate
396 // new items without overflowing
397 throw new ArrayIndexOutOfBoundsException(
398 "coords exceeds maximum capacity !");
399 }
400 // growth algorithm computation
401 int grow = oldSize;
402 if (grow > EXPAND_MAX_COORDS) {
403 grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
404 } else if (grow < INIT_SIZE) {
405 grow = INIT_SIZE; // ensure > 6 (cubics)
406 }
407 assert grow > needed;
408
409 int newSize = oldSize + grow;
410 if (newSize < newSizeMin) {
411 // overflow in growth algorithm computation
412 newSize = Integer.MAX_VALUE;
413 }
414 while (true) {
415 try {
416 // try allocating the larger array
417 return Arrays.copyOf(oldCoords, newSize);
418 } catch (OutOfMemoryError oome) {
419 if (newSize == newSizeMin) {
420 throw oome;
421 }
422 }
423 newSize = newSizeMin + (newSize - newSizeMin) / 2;
424 }
425 }
426
427 /**
428 * Adds a point to the path by moving to the specified
429 * coordinates specified in float precision.
430 *
431 * @param x the specified X coordinate
432 * @param y the specified Y coordinate
433 */
434 public final void moveTo(float x, float y) {
435 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
436 floatCoords[numCoords-2] = moveX = prevX = currX = x;
437 floatCoords[numCoords-1] = moveY = prevY = currY = y;
438 } else {
439 needRoom(false, 2);
440 pointTypes[numTypes++] = SEG_MOVETO;
441 floatCoords[numCoords++] = moveX = prevX = currX = x;
442 floatCoords[numCoords++] = moveY = prevY = currY = y;
443 }
2330 int typeIdx;
2331 int pointIdx;
2332 Path2D path;
2333
2334 Iterator(Path2D path) {
2335 this.path = path;
2336 }
2337
2338 public int getWindingRule() {
2339 return path.getWindingRule();
2340 }
2341
2342 public boolean isDone() {
2343 return (typeIdx >= path.numTypes);
2344 }
2345
2346 public void next() {
2347 int type = path.pointTypes[typeIdx++];
2348 pointIdx += curvecoords[type];
2349 }
2350 }
2351
2352 public void setTo(Path2D otherPath) {
2353 numTypes = otherPath.numTypes;
2354 numCoords = otherPath.numCoords;
2355 if (numTypes > pointTypes.length) {
2356 pointTypes = new byte[numTypes];
2357 }
2358 System.arraycopy(otherPath.pointTypes, 0, pointTypes, 0, numTypes);
2359 if (numCoords > floatCoords.length) {
2360 floatCoords = new float[numCoords];
2361 }
2362 System.arraycopy(otherPath.floatCoords, 0, floatCoords, 0, numCoords);
2363 windingRule = otherPath.windingRule;
2364 moveX = otherPath.moveX;
2365 moveY = otherPath.moveY;
2366 prevX = otherPath.prevX;
2367 prevY = otherPath.prevY;
2368 currX = otherPath.currX;
2369 currY = otherPath.currY;
|