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+ operations .addAll (path .operations );
124+ if (flatness != 0 ) {
125+ operations .add (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 applyOnAnyHandle (handle -> {
302+ return handle .contains (x , y , gc , outline );
303+ });
315304}
316305
317306/**
@@ -330,26 +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 ) {
345- zoomToHandle .entrySet ().removeIf (entry -> {
346- final Integer zoom = entry .getKey ();
347- if (!zoomLevels .contains (zoom ) && zoom != initialZoom ) {
348- entry .getValue ().destroy ();
349- return true ;
350- }
351- return false ;
352- });
335+ // As long as we keep the operations, we can cleanup all handles
336+ zoomToHandle .values ().forEach (PathHandle ::destroy );
337+ zoomToHandle .clear ();
353338}
354339
355340/**
@@ -371,8 +356,10 @@ public void getBounds (float[] bounds) {
371356 if (bounds == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
372357 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
373358 if (bounds .length < 4 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
374- PathHandle handle = getPathHandle (initialZoom );
375- handle .fillBounds (bounds );
359+ applyOnAnyHandle (handle -> {
360+ handle .fillBounds (bounds );
361+ return true ;
362+ });
376363}
377364
378365/**
@@ -393,8 +380,10 @@ public void getCurrentPoint (float[] point) {
393380 if (point == null ) SWT .error (SWT .ERROR_NULL_ARGUMENT );
394381 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
395382 if (point .length < 2 ) SWT .error (SWT .ERROR_INVALID_ARGUMENT );
396- PathHandle handle = getPathHandle (initialZoom );
397- handle .fillCurrentPoint (point );
383+ applyOnAnyHandle (handle -> {
384+ handle .fillCurrentPoint (point );
385+ return true ;
386+ });
398387}
399388
400389/**
@@ -410,8 +399,9 @@ public void getCurrentPoint (float[] point) {
410399 */
411400public PathData getPathData () {
412401 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
413- PathHandle handle = getPathHandle (initialZoom );
414- return handle .getPathData ();
402+ return applyOnAnyHandle (handle -> {
403+ return handle .getPathData ();
404+ });
415405}
416406
417407/**
@@ -427,7 +417,7 @@ public PathData getPathData() {
427417 */
428418public void lineTo (float x , float y ) {
429419 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
430- applyOperationForAllHandles (new LineToOperation (x , y ));
420+ storeAndApplyOperationOnAllHandles (new LineToOperation (x , y ));
431421}
432422
433423
@@ -470,7 +460,7 @@ void init(PathData data) {
470460 */
471461@ Override
472462public boolean isDisposed () {
473- return zoomToHandle . isEmpty () ;
463+ return this . isDestroyed ;
474464}
475465
476466/**
@@ -487,7 +477,7 @@ public boolean isDisposed() {
487477 */
488478public void moveTo (float x , float y ) {
489479 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
490- applyOperationForAllHandles (new MoveToOperation (x , y ));
480+ storeAndApplyOperationOnAllHandles (new MoveToOperation (x , y ));
491481}
492482
493483/**
@@ -504,24 +494,25 @@ public void moveTo (float x, float y) {
504494 */
505495public void quadTo (float cx , float cy , float x , float y ) {
506496 if (isDisposed ()) SWT .error (SWT .ERROR_GRAPHIC_DISPOSED );
507- applyOperationForAllHandles (new QuadToOperation (cx , cy , x , y ));
497+ storeAndApplyOperationOnAllHandles (new QuadToOperation (cx , cy , x , y ));
508498}
509499
510- private class PathHandle {
511- private long handle ;
512- private int zoom ;
500+ private static class PathHandle {
501+ private final Device device ;
502+ private final long handle ;
503+ private final int zoom ;
513504 private PointF currentPoint = new PointF ();
514505 private PointF startPoint = new PointF ();
515506
516- public PathHandle (final long handle , final int zoom ) {
507+ public PathHandle (final Device device , final long handle , final int zoom ) {
508+ this .device = device ;
517509 this .handle = handle ;
518510 this .zoom = zoom ;
519511 }
520512
521513 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 );
514+ float xInPixels = DPIUtil .scaleUp (device , x , zoom );
515+ float yInPixels = DPIUtil .scaleUp (device , y , zoom );
525516 return containsInPixels (xInPixels , yInPixels , gc , outline );
526517 }
527518
@@ -544,7 +535,7 @@ void destroy() {
544535
545536 void fillBounds (float [] bounds ) {
546537 getBoundsInPixels (bounds );
547- float [] scaledbounds = DPIUtil .scaleDown (getDevice () , bounds , zoom );
538+ float [] scaledbounds = DPIUtil .scaleDown (device , bounds , zoom );
548539 System .arraycopy (scaledbounds , 0 , bounds , 0 , 4 );
549540 }
550541
@@ -559,7 +550,7 @@ private void getBoundsInPixels(float[] bounds) {
559550
560551 void fillCurrentPoint (float [] point ) {
561552 getCurrentPointInPixels (point );
562- float [] scaledpoint = DPIUtil .scaleDown (getDevice () , point , zoom );
553+ float [] scaledpoint = DPIUtil .scaleDown (device , point , zoom );
563554 System .arraycopy (scaledpoint , 0 , point , 0 , 2 );
564555 }
565556
@@ -570,7 +561,7 @@ private void getCurrentPointInPixels(float[] point) {
570561
571562 PathData getPathData () {
572563 PathData result = getPathDataInPixels ();
573- result .points = DPIUtil .scaleDown (getDevice () , result .points , zoom );
564+ result .points = DPIUtil .scaleDown (device , result .points , zoom );
574565 return result ;
575566 }
576567
@@ -721,18 +712,20 @@ private void addRectangleInPixels(PathHandle pathHandle, float x, float y, float
721712}
722713
723714private class AddPathOperation implements Operation {
724- private final Path path ;
715+ private final List < Operation > operations ;
725716
726717 public AddPathOperation (Path path ) {
727- this .path = path ;
718+ this .operations = path . operations ;
728719 }
729720
730721 @ Override
731722 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 ;
723+ applyOnTemporaryHandle (getDevice (), pathHandle .zoom , operations , temporaryHandle -> {
724+ Gdip .GraphicsPath_AddPath (pathHandle .handle , temporaryHandle .handle , false );
725+ pathHandle .currentPoint .X = temporaryHandle .currentPoint .X ;
726+ pathHandle .currentPoint .Y = temporaryHandle .currentPoint .Y ;
727+ return true ;
728+ });
736729 }
737730}
738731
@@ -798,6 +791,14 @@ public void apply(PathHandle pathHandle) {
798791 }
799792}
800793
794+ private record FlattenOperation (float flatness ) implements Operation {
795+ @ Override
796+ public void apply (PathHandle pathHandle ) {
797+ long handle = pathHandle .handle ;
798+ Gdip .GraphicsPath_Flatten (handle , 0 , flatness );
799+ }
800+ }
801+
801802private class CubicToOperation implements Operation {
802803 private final float cx1 ;
803804 private final float cy1 ;
@@ -926,10 +927,31 @@ private interface Operation {
926927 void apply (PathHandle pathHandle );
927928}
928929
929- private void applyOperationForAllHandles (Operation operation ) {
930+ private void storeAndApplyOperationOnAllHandles (Operation operation ) {
931+ operations .add (operation );
930932 zoomToHandle .values ().forEach (operation ::apply );
931933}
932934
935+ private <T > T applyOnAnyHandle (Function <PathHandle , T > function ) {
936+ if (zoomToHandle .isEmpty ()) {
937+ return applyOnTemporaryHandle (getDevice (), DPIUtil .getDeviceZoom (), this .operations , function );
938+ } else {
939+ return function .apply (zoomToHandle .values ().iterator ().next ());
940+ }
941+ }
942+
943+ private static <T > T applyOnTemporaryHandle (Device device , int zoom , List <Operation > operations , Function <PathHandle , T > function ) {
944+ PathHandle temporaryHandle = newEmptyPathHandle (device , zoom );
945+ try {
946+ for (Operation operation : operations ) {
947+ operation .apply (temporaryHandle );
948+ }
949+ return function .apply (temporaryHandle );
950+ } finally {
951+ temporaryHandle .destroy ();
952+ }
953+ }
954+
933955/**
934956 * Returns a string containing a concise, human-readable
935957 * description of the receiver.
@@ -942,14 +964,26 @@ public String toString() {
942964 return "Path " + zoomToHandle ;
943965}
944966
967+ private static PathHandle newEmptyPathHandle (Device device , int zoom ) {
968+ long newHandle = Gdip .GraphicsPath_new (Gdip .FillModeAlternate );
969+ if (newHandle == 0 ) SWT .error (SWT .ERROR_NO_HANDLES );
970+ PathHandle newPathHandle = new PathHandle (device , newHandle , zoom );
971+ return newPathHandle ;
972+ }
973+
974+ private PathHandle newPathHandle (int zoom ) {
975+ PathHandle newPathHandle = newEmptyPathHandle (getDevice (), zoom );
976+ for (Operation operation : operations ) {
977+ operation .apply (newPathHandle );
978+ }
979+ return newPathHandle ;
980+ }
981+
945982private PathHandle getPathHandle (int zoom ) {
946983 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 ;
984+ PathHandle newHandle = newPathHandle (zoom );
985+ zoomToHandle .put (zoom , newHandle );
986+ return newHandle ;
953987 }
954988 return zoomToHandle .get (zoom );
955989}
0 commit comments