Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8228698

Incorrectly calculated bounds for Arc shape with stroke width 5

XMLWordPrintable

    • x86_64
    • windows_10

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10, Java 11.0.4

      A DESCRIPTION OF THE PROBLEM :
      An arc, with radius x/y 22, and a stroke width of 5, should always fit in a 50x50 pixel box. However, quite often its total width/height slightly exceeds this value (seen with getBoundsInLocal). Sometimes however it exceeds this value by a lot (sometimes reaching 58 pixels high or wide), as demonstrated by the attached test case.

      Sample values this test prints:

      arc: Arc[centerX=0.0, centerY=0.0, radiusX=22.0, radiusY=22.0, startAngle=90.0, length=-182.3427, type=OPEN, fill=0x00000000, stroke=0x7dff7d80, strokeWidth=5.0]

      arc: BoundingBox [minX:-3.9993820190429688, minY:-33.319679260253906, minZ:0.0, width:28.9993896484375, height:58.31974792480469, depth:0.0, maxX:25.00000762939453, maxY:25.00006866455078, maxZ:0.0]

      The height of 58.3 is completely unexplainable given the parameters the Arc has.

      This happens infrequently, but usually within 60 seconds of running the test case, which simply animates the arc until the bug occurs.

      Tracing the problem ends up somewhere deep in the Shape class (The Arc and Arc2D class seem to not cause this issue). I suspect the issue may be in "Toolkit.getToolkit().accumulateStrokeBounds()" somewhere and could be time sensitive.

      The problem does not occur with a stroke-width of 1 or 2 (there is no visible "jumping" of the circle while animated. It starts occuring at width 3, and more frequently at 4 and 5.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1) Run attached program
      2) Observe slight jumps in the circle positioning, indicative of the problem
      3) Wait until the animation is halted and debug prints appear on the console.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No positioning jumps should occur, the local bounds calculation should return predictable results.
      ACTUAL -
      Positioning of the arc fluctuates, often slightly, sometimes by multiple pixels. The local bounds calculation returns obviously incorrect bounds when a bad position is shown on screen.

      ---------- BEGIN SOURCE ----------
      package hs.javafxbug;

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.Executors;
      import java.util.concurrent.TimeUnit;

      import javafx.animation.KeyFrame;
      import javafx.animation.KeyValue;
      import javafx.animation.Timeline;
      import javafx.application.Application;
      import javafx.application.Platform;
      import javafx.beans.property.DoubleProperty;
      import javafx.beans.property.SimpleDoubleProperty;
      import javafx.geometry.Bounds;
      import javafx.geometry.Pos;
      import javafx.scene.Group;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.ContentDisplay;
      import javafx.scene.control.Label;
      import javafx.scene.layout.StackPane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Arc;
      import javafx.scene.shape.ArcType;
      import javafx.scene.shape.Rectangle;
      import javafx.stage.Stage;
      import javafx.util.Duration;

      public class JavaFXBug extends Application {

        public static void main(String[] args) {
          Application.launch(args);
        }

        @Override
        public void start(Stage primaryStage) throws Exception {
          MediaStatusIndicatorPane indicator = new MediaStatusIndicatorPane();

          primaryStage.setScene(new Scene(indicator));
          primaryStage.show();

          Timeline tl = new Timeline(
            new KeyFrame(Duration.ZERO, new KeyValue(indicator.value, 0.1)),
            new KeyFrame(Duration.seconds(2), new KeyValue(indicator.value, 0.99))
          );

          tl.setAutoReverse(true);
          tl.setCycleCount(Timeline.INDEFINITE);
          tl.play();

          //
          // Background thread which frequently checks bounds of the arc group,
          // and stops the animation if the bounds are wrong. Bounds height
          // should never exceed 50 (but it often does so slightly). Sometimes
          // however it exceeds it by multiple pixels, causing jumps...
          //
          // The Width of the bounds also has this problem.
          //
          Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(
            () -> Platform.runLater(() -> {
              Bounds boundsInLocal = indicator.group.getBoundsInLocal();

              if(boundsInLocal.getHeight() > 54) {
                System.out.println(boundsInLocal);
                System.out.println("arc: " + indicator.arc);
                System.out.println("arc: " + indicator.arc.getBoundsInLocal());
                tl.stop();
              }
            }),
            0,
            37,
            TimeUnit.MILLISECONDS
          );
        }

      }

      class MediaStatusIndicatorPane extends StackPane {
        public final DoubleProperty value = new SimpleDoubleProperty(0.0);

        public Group group;
        public Arc arc;

        public MediaStatusIndicatorPane() {
          value.addListener(obs -> {
            double percentage = value.get();

            List<Node> children = new ArrayList<>();
            Label bg = new Label();

            bg.setStyle("-fx-background-color: rgba(0,0,0,0.6); -fx-scale-x: 1.1; -fx-scale-y: 1.1; -fx-min-width: 50; -fx-min-height: 50;");

            children.add(bg);

            arc = new Arc(0, 0, 22, 22, 90, -360 * percentage);

            arc.setStyle("-fx-stroke-width: 5; -fx-fill: transparent; -fx-stroke: rgba(125, 255, 125, 0.5)");
            arc.setType(ArcType.OPEN);

            Rectangle rectangle = new Rectangle(-25, -25, 50, 50);

            rectangle.setFill(new Color(1, 0, 0, 0.3));

            Label arcGraphic = new Label();

            group = new Group(rectangle, arc);
            arcGraphic.setGraphic(group);
            arcGraphic.setAlignment(Pos.TOP_CENTER);
            arcGraphic.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

            children.add(arcGraphic);

            getChildren().setAll(children);
          });
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      None that I found, apart from rendering my own arc or setting stroke-width to 1.

      FREQUENCY : always


            kcr Kevin Rushforth
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: