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

[Canvas] GraphicsContext set transform not working as expected

    Details

      Description

      package canvastest;

      import java.awt.geom.Rectangle2D;

      import javafx.application.Application;
      import javafx.event.ActionEvent;
      import javafx.scene.Scene;
      import javafx.scene.canvas.Canvas;
      import javafx.scene.canvas.GraphicsContext;
      import javafx.scene.control.Button;
      import javafx.scene.control.ScrollPane;
      import javafx.scene.control.ScrollPane.ScrollBarPolicy;
      import javafx.scene.control.ToolBar;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.StrokeLineCap;
      import javafx.scene.shape.StrokeLineJoin;
      import javafx.scene.transform.Affine;
      import javafx.stage.Stage;

      public class JFXMapRenderingTest extends Application {

      /** X coordinates of points to testing */
      private final static double[] XPoints = { 839688.142, 842040.845, 842067.716, 839828.468, 840900.322, 840748.053 };
      /** Y coordinates of points to testing */
      private final static double[] YPoints = { 834278.158, 834322.943, 835979.987, 835978.944, 835224.614, 835129.073 };

      /** Max map extent: [ xmin, xmax, ymin, ymax ] */
      private final static double[] MaxMapExtent = { 839650.414, 842180.086, 834204.376, 835988.085 };
      /** Current map extent: [ xmin, xmax, ymin, ymax ] */
      private final static double[] CurrentMapExtent = { 839650.414, 842180.086, 833831.3944999999, 836361.0665 };
      /** Viewport extent: [ xmin, xmax, ymin, ymax ] */
      private static Rectangle2D.Double viewport;

      /** Map Scale X */
      private static double mapscaleX = 1.0;
      /** Map Scale Y */
      private static double mapscaleY = 1.0;
      /** Map Scale */
      private static double mapscale = 1.0;
      /** Map Translation X*/
      private static double maptranslateX = 0.0;
      /** Map Translation Y*/
      private static double maptranslateY = 0.0;

      @Override
      public void start(Stage primaryStage) {
      primaryStage.setTitle("JFX Canvas Map Rendering Test");
      BorderPane root = new BorderPane();
      Scene scene = new Scene(root, 500, 533);

      ScrollPane scrollPane = new ScrollPane();
      scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
      scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
      root.setCenter(scrollPane);

      Canvas canvas = new Canvas();
      canvas.widthProperty().bind(scrollPane.widthProperty());
      canvas.heightProperty().bind(scrollPane.heightProperty());
      scrollPane.setContent(canvas);
      canvas.setScaleY(-1); // change the origin(0, 0) to lower left

      Button btnDraw = new Button("Draw");
      btnDraw.setOnAction((ActionEvent event) -> {
      clear(canvas);
      draw(canvas);
      });

      Button btnClear = new Button("Clear");
      btnClear.setOnAction((ActionEvent event) -> {
      clear(canvas);
      });

      Button btnFullExtent = new Button("FullExtent");
      btnFullExtent.setOnAction((ActionEvent event) -> {
      double scalex = canvas.getScaleX();
      double scaley = canvas.getScaleY();
      scalex = scalex > 0 ? 1.0 : -1.0;
      scaley = scaley > 0 ? 1.0 : -1.0;
      canvas.setScaleX(scalex);
      canvas.setScaleY(scaley);
      // draw(canvas);
      });

      Button btnZoomin = new Button("ZoomIn");
      btnZoomin.setOnAction((ActionEvent event) -> {
      canvas.setScaleX(2 * canvas.getScaleX());
      canvas.setScaleY(2 * canvas.getScaleY());
      // draw(canvas);
      });

      Button btnZoomout = new Button("ZoomOut");
      btnZoomout.setOnAction((ActionEvent event) -> {
      canvas.setScaleX(0.5 * canvas.getScaleX());
      canvas.setScaleY(0.5 * canvas.getScaleY());
      // draw(canvas);
      });

      ToolBar toolbar = new ToolBar(btnDraw, btnClear, btnFullExtent, btnZoomin, btnZoomout);
      root.setTop(toolbar);

      primaryStage.setScene(scene);
      primaryStage.show();

      viewport = new Rectangle2D.Double(0, 0, canvas.getWidth(), canvas.getHeight());
      }

      private void clear(Canvas canvas) {
      double xmin = MaxMapExtent[0];
      double xmax = MaxMapExtent[1];
      double ymin = MaxMapExtent[2];
      double ymax = MaxMapExtent[3];
      double width = xmax - xmin;
      double height = ymax - ymin;
      GraphicsContext gc = canvas.getGraphicsContext2D();
      gc.clearRect(xmin, ymin, width, height);
      }

      private void draw(Canvas canvas) {
      GraphicsContext gc = canvas.getGraphicsContext2D();

      // Adjust the scope of the current map to match the width to height ratio of the viewport
      double aspectRatio = viewport.width / viewport.height;
      double xmin = CurrentMapExtent[0];
      double xmax = CurrentMapExtent[1];
      double ymin = CurrentMapExtent[2];
      double ymax = CurrentMapExtent[3];
      double w = (xmax - xmin);
      double h = (ymax - ymin);
      if (w < h) {
      xmin = CurrentMapExtent[0] -= Math.abs((w - aspectRatio * h) / 2);
      xmax = CurrentMapExtent[1] += Math.abs((w - aspectRatio * h) / 2);
      } else {
      ymin = CurrentMapExtent[2] -= Math.abs((h - w / aspectRatio) / 2);
      ymax = CurrentMapExtent[3] += Math.abs((h - w / aspectRatio) / 2);
      }
      w = (xmax - xmin);
      h = (ymax - ymin);

      mapscaleX = canvas.getWidth() / w;
      mapscaleY = canvas.getHeight() / h;
      mapscale = mapscaleX > mapscaleY ? mapscaleX : mapscaleY;
      maptranslateX = canvas.getTranslateX() - xmin; // 0 - xmin
      maptranslateY = canvas.getTranslateY() - ymin; // 0 - ymin

      /** No.1: why can't do like this??? what's the different with the No.2??? */
      // gc.setTransform(mapscale, 0.0, 0.0, mapscale, maptranslateX, maptranslateY);

      /** No.2: this will be ok */
      // gc.setTransform(mapscale, 0.0, 0.0, mapscale, 0, 0);
      // gc.translate(maptranslateX, maptranslateY);


      /** No.3: why can't do like this??? what's the different with the No.4??? */
      // Affine affine = new Affine(mapscale, 0.0, maptranslateX, 0.0, mapscaleY, maptranslateY);
      // gc.setTransform(affine);

      /** No.4: this will be ok */
      Affine affine = new Affine();
      affine.appendScale(mapscale, mapscale);
      affine.appendTranslation(maptranslateX, maptranslateY);
      gc.setTransform(affine);

      System.out.println("start rendering...");
      long st = System.currentTimeMillis();

      gc.setFill(Color.GREEN);
      gc.setStroke(Color.ORANGE);
      gc.setLineCap(StrokeLineCap.ROUND);
      gc.setLineJoin(StrokeLineJoin.ROUND);
      gc.setLineWidth(2.0);

      gc.strokePolyline(XPoints, YPoints, XPoints.length);

      System.out.println("Time consuming: " + (System.currentTimeMillis() - st) / 1000.0 + " s\n");
      }

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

      }

      Question 1: please see draw() method that like "No.1, No.2 ..." etc.
      Question 2: when I call the draw() method first, the line width looks very well... but from after the second, the line width looks bad and different with the first one, why??

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                flar Jim Graham
                Reporter:
                duke J. Duke (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Imported: