1414package org .eclipse .swt .graphics ;
1515
1616import java .util .*;
17+ import java .util .function .*;
1718
1819import org .eclipse .swt .*;
1920import org .eclipse .swt .internal .*;
4243 * @since 3.1
4344 */
4445public class Path extends Resource {
45- private int initialZoom ;
46-
4746 private Map <Integer , PathHandle > zoomToHandle = new HashMap <>();
4847
48+ private List <Operation > operations = new ArrayList <>();
49+
50+ private boolean isDestroyed ;
51+
4952/**
5053 * Constructs a new empty Path.
5154 * <p>
@@ -72,16 +75,8 @@ public class Path extends Resource {
7275 * @see #dispose()
7376 */
7477public Path (Device device ) {
75- this (device , DPIUtil .getDeviceZoom ());
76- }
77-
78- private Path (Device device , int zoom ) {
7978 super (device );
8079 this .device .checkGDIP ();
81- initialZoom = zoom ;
82- long handle = Gdip .GraphicsPath_new (Gdip .FillModeAlternate );
83- if (handle == 0 ) SWT .error (SWT .ERROR_NO_HANDLES );
84- zoomToHandle .put (initialZoom , new PathHandle (handle , initialZoom ));
8580 init ();
8681 this .device .registerResourceWithZoomSupport (this );
8782}
@@ -125,11 +120,10 @@ public Path (Device device, Path path, float flatness) {
125120 if (path == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
126121 if (path .isDisposed ()) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
127122 flatness = Math .max (0 , flatness );
128- long handle = Gdip .GraphicsPath_Clone (path .getHandle (path .initialZoom ));
129- if (flatness != 0 ) Gdip .GraphicsPath_Flatten (handle , 0 , flatness );
130- if (handle == 0 ) SWT .error (SWT .ERROR_NO_HANDLES );
131- initialZoom = path .initialZoom ;
132- zoomToHandle .put (initialZoom , new PathHandle (handle , initialZoom ));
123+ path .operations .forEach (this ::storeAndApplyOperationOnAllHandles );
124+ if (flatness != 0 ) {
125+ storeAndApplyOperationOnAllHandles (new FlattenOperation (flatness ));
126+ }
133127 init ();
134128 this .device .registerResourceWithZoomSupport (this );
135129}
@@ -163,15 +157,9 @@ public Path (Device device, Path path, float flatness) {
163157 * @since 3.4
164158 */
165159public Path (Device device , PathData data ) {
166- this (device , data , DPIUtil .getDeviceZoom ());
167-
168- }
169-
170- private Path (Device device , PathData data , int zoom ) {
171- this (device , zoom );
160+ this (device );
172161 if (data == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
173162 init (data );
174- this .device .registerResourceWithZoomSupport (this );
175163}
176164
177165/**
@@ -205,7 +193,7 @@ private Path(Device device, PathData data, int zoom) {
205193 */
206194public void addArc (float x , float y , float width , float height , float startAngle , float arcAngle ) {
207195 if (width == 0 || height == 0 || arcAngle == 0 ) return ;
208- applyOperationForAllHandles (new AddArcOperation (x , y , width , height , startAngle , arcAngle ));
196+ storeAndApplyOperationOnAllHandles (new AddArcOperation (x , y , width , height , startAngle , arcAngle ));
209197}
210198
211199/**
@@ -225,7 +213,7 @@ public void addPath(Path path) {
225213 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
226214 if (path == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
227215 if (path .isDisposed ()) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
228- applyOperationForAllHandles (new AddPathOperation (path ));
216+ storeAndApplyOperationOnAllHandles (new AddPathOperation (path ));
229217}
230218
231219/**
@@ -242,7 +230,7 @@ public void addPath(Path path) {
242230 */
243231public void addRectangle (float x , float y , float width , float height ) {
244232 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
245- applyOperationForAllHandles (new AddRectangleOperation (x , y , width , height ));
233+ storeAndApplyOperationOnAllHandles (new AddRectangleOperation (x , y , width , height ));
246234}
247235
248236/**
@@ -266,7 +254,7 @@ public void addString (String string, float x, float y, Font font) {
266254 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
267255 if (font == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
268256 if (font .isDisposed ()) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
269- applyOperationForAllHandles (new AddStringOperation (string , x , y , font ));
257+ storeAndApplyOperationOnAllHandles (new AddStringOperation (string , x , y , font ));
270258}
271259
272260/**
@@ -280,7 +268,7 @@ public void addString (String string, float x, float y, Font font) {
280268 */
281269public void close () {
282270 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
283- applyOperationForAllHandles (new CloseOperation ());
271+ storeAndApplyOperationOnAllHandles (new CloseOperation ());
284272}
285273
286274/**
@@ -310,8 +298,9 @@ public boolean contains (float x, float y, GC gc, boolean outline) {
310298 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
311299 if (gc == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
312300 if (gc .isDisposed ()) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
313- PathHandle handle = getPathHandle (initialZoom );
314- return handle .contains (x , y , gc , outline );
301+ return applyUsingAnyHandle (handle -> {
302+ return handle .contains (x , y , gc , outline );
303+ });
315304}
316305
317306/**
@@ -330,21 +319,22 @@ public boolean contains (float x, float y, GC gc, boolean outline) {
330319 */
331320public void cubicTo (float cx1 , float cy1 , float cx2 , float cy2 , float x , float y ) {
332321 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
333- applyOperationForAllHandles (new CubicToOperation (cx1 , cy1 , cx2 , cy2 , x , y ));
322+ storeAndApplyOperationOnAllHandles (new CubicToOperation (cx1 , cy1 , cx2 , cy2 , x , y ));
334323}
335324
336325@ Override
337326void destroy () {
338327 device .deregisterResourceWithZoomSupport (this );
339328 zoomToHandle .values ().forEach (PathHandle ::destroy );
340329 zoomToHandle .clear ();
330+ this .isDestroyed = true ;
341331}
342332
343333@ Override
344334void destroyHandlesExcept (Set <Integer > zoomLevels ) {
345335 zoomToHandle .entrySet ().removeIf (entry -> {
346336 final Integer zoom = entry .getKey ();
347- if (!zoomLevels .contains (zoom ) && zoom != initialZoom ) {
337+ if (!zoomLevels .contains (zoom )) {
348338 entry .getValue ().destroy ();
349339 return true ;
350340 }
@@ -371,8 +361,10 @@ public void getBounds (float[] bounds) {
371361 if (bounds == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
372362 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
373363 if (bounds .length < 4 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
374- PathHandle handle = getPathHandle (initialZoom );
375- handle .fillBounds (bounds );
364+ applyUsingAnyHandle (handle -> {
365+ handle .fillBounds (bounds );
366+ return true ;
367+ });
376368}
377369
378370/**
@@ -393,8 +385,10 @@ public void getCurrentPoint (float[] point) {
393385 if (point == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
394386 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
395387 if (point .length < 2 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
396- PathHandle handle = getPathHandle (initialZoom );
397- handle .fillCurrentPoint (point );
388+ applyUsingAnyHandle (handle -> {
389+ handle .fillCurrentPoint (point );
390+ return true ;
391+ });
398392}
399393
400394/**
@@ -410,8 +404,9 @@ public void getCurrentPoint (float[] point) {
410404 */
411405public PathData getPathData () {
412406 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
413- PathHandle handle = getPathHandle (initialZoom );
414- return handle .getPathData ();
407+ return applyUsingAnyHandle (handle -> {
408+ return handle .getPathData ();
409+ });
415410}
416411
417412/**
@@ -427,7 +422,7 @@ public PathData getPathData() {
427422 */
428423public void lineTo (float x , float y ) {
429424 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
430- applyOperationForAllHandles (new LineToOperation (x , y ));
425+ storeAndApplyOperationOnAllHandles (new LineToOperation (x , y ));
431426}
432427
433428
@@ -470,7 +465,7 @@ void init(PathData data) {
470465 */
471466@ Override
472467public boolean isDisposed () {
473- return zoomToHandle . isEmpty () ;
468+ return this . isDestroyed ;
474469}
475470
476471/**
@@ -487,7 +482,7 @@ public boolean isDisposed() {
487482 */
488483public void moveTo (float x , float y ) {
489484 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
490- applyOperationForAllHandles (new MoveToOperation (x , y ));
485+ storeAndApplyOperationOnAllHandles (new MoveToOperation (x , y ));
491486}
492487
493488/**
@@ -504,24 +499,25 @@ public void moveTo (float x, float y) {
504499 */
505500public void quadTo (float cx , float cy , float x , float y ) {
506501 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
507- applyOperationForAllHandles (new QuadToOperation (cx , cy , x , y ));
502+ storeAndApplyOperationOnAllHandles (new QuadToOperation (cx , cy , x , y ));
508503}
509504
510- private class PathHandle {
511- private long handle ;
512- private int zoom ;
505+ private static class PathHandle {
506+ private final Device device ;
507+ private final long handle ;
508+ private final int zoom ;
513509 private PointF currentPoint = new PointF ();
514510 private PointF startPoint = new PointF ();
515511
516- public PathHandle (final long handle , final int zoom ) {
512+ public PathHandle (final Device device , final long handle , final int zoom ) {
513+ this .device = device ;
517514 this .handle = handle ;
518515 this .zoom = zoom ;
519516 }
520517
521518 boolean contains (float x , float y , GC gc , boolean outline ) {
522- Drawable drawable = getDevice ();
523- float xInPixels = DPIUtil .scaleUp (drawable , x , zoom );
524- float yInPixels = DPIUtil .scaleUp (drawable , y , zoom );
519+ float xInPixels = DPIUtil .scaleUp (device , x , zoom );
520+ float yInPixels = DPIUtil .scaleUp (device , y , zoom );
525521 return containsInPixels (xInPixels , yInPixels , gc , outline );
526522 }
527523
@@ -544,7 +540,7 @@ void destroy() {
544540
545541 void fillBounds (float [] bounds ) {
546542 getBoundsInPixels (bounds );
547- float [] scaledbounds = DPIUtil .scaleDown (getDevice () , bounds , zoom );
543+ float [] scaledbounds = DPIUtil .scaleDown (device , bounds , zoom );
548544 System .arraycopy (scaledbounds , 0 , bounds , 0 , 4 );
549545 }
550546
@@ -559,7 +555,7 @@ private void getBoundsInPixels(float[] bounds) {
559555
560556 void fillCurrentPoint (float [] point ) {
561557 getCurrentPointInPixels (point );
562- float [] scaledpoint = DPIUtil .scaleDown (getDevice () , point , zoom );
558+ float [] scaledpoint = DPIUtil .scaleDown (device , point , zoom );
563559 System .arraycopy (scaledpoint , 0 , point , 0 , 2 );
564560 }
565561
@@ -570,7 +566,7 @@ private void getCurrentPointInPixels(float[] point) {
570566
571567 PathData getPathData () {
572568 PathData result = getPathDataInPixels ();
573- result .points = DPIUtil .scaleDown (getDevice () , result .points , zoom );
569+ result .points = DPIUtil .scaleDown (device , result .points , zoom );
574570 return result ;
575571 }
576572
@@ -721,18 +717,20 @@ private void addRectangleInPixels(PathHandle pathHandle, float x, float y, float
721717}
722718
723719private class AddPathOperation implements Operation {
724- private final Path path ;
720+ private final List < Operation > pathOperations ;
725721
726722 public AddPathOperation (Path path ) {
727- this .path = path ;
723+ this .pathOperations = path . operations ;
728724 }
729725
730726 @ Override
731727 public void apply (PathHandle pathHandle ) {
732- PathHandle secondPathHandle = path .getPathHandle (pathHandle .zoom );
733- Gdip .GraphicsPath_AddPath (pathHandle .handle , secondPathHandle .handle , false );
734- pathHandle .currentPoint .X = secondPathHandle .currentPoint .X ;
735- pathHandle .currentPoint .Y = secondPathHandle .currentPoint .Y ;
728+ applyOnTemporaryHandle (getDevice (), pathHandle .zoom , pathOperations , temporaryHandle -> {
729+ Gdip .GraphicsPath_AddPath (pathHandle .handle , temporaryHandle .handle , false );
730+ pathHandle .currentPoint .X = temporaryHandle .currentPoint .X ;
731+ pathHandle .currentPoint .Y = temporaryHandle .currentPoint .Y ;
732+ return true ;
733+ });
736734 }
737735}
738736
@@ -798,6 +796,14 @@ public void apply(PathHandle pathHandle) {
798796 }
799797}
800798
799+ private record FlattenOperation (float flatness ) implements Operation {
800+ @ Override
801+ public void apply (PathHandle pathHandle ) {
802+ long handle = pathHandle .handle ;
803+ Gdip .GraphicsPath_Flatten (handle , 0 , flatness );
804+ }
805+ }
806+
801807private class CubicToOperation implements Operation {
802808 private final float cx1 ;
803809 private final float cy1 ;
@@ -926,10 +932,31 @@ private interface Operation {
926932 void apply (PathHandle pathHandle );
927933}
928934
929- private void applyOperationForAllHandles (Operation operation ) {
935+ private void storeAndApplyOperationOnAllHandles (Operation operation ) {
936+ operations .add (operation );
930937 zoomToHandle .values ().forEach (operation ::apply );
931938}
932939
940+ private <T > T applyUsingAnyHandle (Function <PathHandle , T > function ) {
941+ if (zoomToHandle .isEmpty ()) {
942+ return applyOnTemporaryHandle (getDevice (), DPIUtil .getDeviceZoom (), this .operations , function );
943+ } else {
944+ return function .apply (zoomToHandle .values ().iterator ().next ());
945+ }
946+ }
947+
948+ private static <T > T applyOnTemporaryHandle (Device device , int zoom , List <Operation > operations , Function <PathHandle , T > function ) {
949+ PathHandle temporaryHandle = newEmptyPathHandle (device , zoom );
950+ try {
951+ for (Operation operation : operations ) {
952+ operation .apply (temporaryHandle );
953+ }
954+ return function .apply (temporaryHandle );
955+ } finally {
956+ temporaryHandle .destroy ();
957+ }
958+ }
959+
933960/**
934961 * Returns a string containing a concise, human-readable
935962 * description of the receiver.
@@ -942,14 +969,26 @@ public String toString() {
942969 return "Path " + zoomToHandle ;
943970}
944971
972+ private static PathHandle newEmptyPathHandle (Device device , int zoom ) {
973+ long newHandle = Gdip .GraphicsPath_new (Gdip .FillModeAlternate );
974+ if (newHandle == 0 ) SWT .error (SWT .ERROR_NO_HANDLES );
975+ PathHandle newPathHandle = new PathHandle (device , newHandle , zoom );
976+ return newPathHandle ;
977+ }
978+
979+ private PathHandle newPathHandle (int zoom ) {
980+ PathHandle newPathHandle = newEmptyPathHandle (getDevice (), zoom );
981+ for (Operation operation : operations ) {
982+ operation .apply (newPathHandle );
983+ }
984+ return newPathHandle ;
985+ }
986+
945987private PathHandle getPathHandle (int zoom ) {
946988 if (!zoomToHandle .containsKey (zoom )) {
947- PathData pathData = getPathData ();
948- Path scaledPath = new Path (getDevice (), pathData , zoom );
949- long handle = scaledPath .getHandle (scaledPath .initialZoom );
950- PathHandle pathHandle = new PathHandle (handle , zoom );
951- zoomToHandle .put (zoom , pathHandle );
952- return pathHandle ;
989+ PathHandle newHandle = newPathHandle (zoom );
990+ zoomToHandle .put (zoom , newHandle );
991+ return newHandle ;
953992 }
954993 return zoomToHandle .get (zoom );
955994}
0 commit comments