Рисунки на холсте становятся размытыми при увеличении - PullRequest
0 голосов
/ 15 октября 2019

Это еще один вопрос о масштабировании и панорамировании, но прежде чем отклонить это, пожалуйста, уделите время прочтению.

Я создаю простое приложение для рисования, в котором мне нужно рисовать поверх изображения. И когда я увеличиваю / уменьшаю чертеж, остаюсь на изображении, где он был нарисован. Я использую ImageViewer для отображения изображения и Canvas для рисунков.

Первой задачей было увеличение и панорамирование изображения, как, например, в Google Maps. Теперь следующая часть - нарисовать что-то поверх изображения. Так как я использовал ImageViewer, я не могу рисовать на нем. Поэтому я добавил Canvas, где я могу рисовать. Проблема заключается в том, что при увеличении масштаба рисунки на холсте становятся размытыми и пикселированными.

Я могу решить эту проблему, используя javaSwing, в этом случае мне необходимо сделать это в FX.

Iпытался применить ту же концепцию, как я это делал на Swing. Как и применение AffineTransform к graphics из Jpanel, но это не работает в FX.

Я нашел что-то , которое также может решить проблему, но я не могупримените к моему проекту, потому что он использует другой алгоритм масштабирования, который противоречит моему.

Я думаю, что некоторый код поможет.

Класс контроллера, основанный на ответе @ jewelsea.

public class Controller implements Initializable {

    final double SCALE_DELTA = 1.1;
    final ObjectProperty<Point2D> lastMouseCoordinates = new SimpleObjectProperty<Point2D>();
    public ScrollPane scroller;
    public Group scrollContent;
    public StackPane zoomPane;
    public Group group;
    public ImageView imageView;
    public Canvas canvas;
    Image image;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        scroller.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> observable,
                                Bounds oldValue, Bounds newValue) {
                zoomPane.setMinSize(newValue.getWidth(), newValue.getHeight());
            }
        });
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.strokeLine(200, 200, 300, 300);
    }

    public void handlePan(MouseEvent event) {
    }

    public void handleZoom(ScrollEvent event) {
        if (event.getDeltaY() == 0) {
            return;
        }
        double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA : 1 / SCALE_DELTA;
        // amount of scrolling in each direction in scrollContent coordinate
        // units
        double oldScale = group.getScaleX();
        double newScale = group.getScaleX() * scaleFactor;
        if (newScale < 1 || newScale > 24) {
            newScale = oldScale;
        }
        Point2D scrollOffset = figureScrollOffset(scrollContent, scroller);
        repositionScroller(scrollContent, scroller, scaleFactor, scrollOffset);
        group.setScaleX(newScale);
        group.setScaleY(newScale);
    }

    public void updateLine(MouseEvent event) {
    }

    public void startDraw(MouseEvent event) {
    }

    public void loadImage(ActionEvent actionEvent) {
        FileChooser fileChooser = new FileChooser();
        File selectedFile = fileChooser.showOpenDialog(Main.stage);

        if (selectedFile == null) {
            JOptionPane.showMessageDialog(null, "No Image selected");
        } else {
            image = new Image("file:" + selectedFile);
            imageView.setImage(image);
        }
    }


    public void getCenterPoint(MouseEvent event) {
        lastMouseCoordinates.set(new Point2D(event.getX(), event.getY()));
    }

    private Point2D figureScrollOffset(Node scrollContent, ScrollPane scroller) {
        double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth();
        double hScrollProportion = (scroller.getHvalue() - scroller.getHmin()) / (scroller.getHmax() - scroller.getHmin());
        double scrollXOffset = hScrollProportion * Math.max(0, extraWidth);
        double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight();
        double vScrollProportion = (scroller.getVvalue() - scroller.getVmin()) / (scroller.getVmax() - scroller.getVmin());
        double scrollYOffset = vScrollProportion * Math.max(0, extraHeight);
        return new Point2D(scrollXOffset, scrollYOffset);
    }

    private void repositionScroller(Node scrollContent, ScrollPane scroller, double scaleFactor, Point2D scrollOffset) {
        double scrollXOffset = scrollOffset.getX();
        double scrollYOffset = scrollOffset.getY();
        double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth();
        if (extraWidth > 0) {
            double halfWidth = scroller.getViewportBounds().getWidth() / 2;
            double newScrollXOffset = (scaleFactor - 1) * halfWidth + scaleFactor * scrollXOffset;
            scroller.setHvalue(scroller.getHmin() + newScrollXOffset * (scroller.getHmax() - scroller.getHmin()) / extraWidth);
        } else {
            scroller.setHvalue(scroller.getHmin());
        }
        double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight();
        if (extraHeight > 0) {
            double halfHeight = scroller.getViewportBounds().getHeight() / 2;
            double newScrollYOffset = (scaleFactor - 1) * halfHeight + scaleFactor * scrollYOffset;
            scroller.setVvalue(scroller.getVmin() + newScrollYOffset * (scroller.getVmax() - scroller.getVmin()) / extraHeight);
        } else {
            scroller.setHvalue(scroller.getHmin());
        }
    }
}

Основной класс.

public class Main extends Application {
    static Stage stage;
    @Override
    public void start(Stage primaryStage) throws Exception {
        stage = primaryStage;
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        primaryStage.setScene(new Scene(root, screenBounds.getWidth(), screenBounds.getHeight()));
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Файл FXML

  <?xml version="1.0" encoding="UTF-8"?>

  <?import javafx.scene.Group?>
  <?import javafx.scene.canvas.Canvas?>
  <?import javafx.scene.control.Button?>
  <?import javafx.scene.control.ScrollPane?>
  <?import javafx.scene.image.Image?>
  <?import javafx.scene.image.ImageView?>
  <?import javafx.scene.layout.AnchorPane?>
  <?import javafx.scene.layout.StackPane?>

  <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="692.0" prefWidth="807.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
     <children>
        <ScrollPane fx:id="scroller" centerShape="false" layoutX="134.0" layoutY="42.0" pannable="true" prefHeight="636.0" prefWidth="666.0" scaleShape="false" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="134.0" AnchorPane.rightAnchor="7.0" AnchorPane.topAnchor="42.0">
           <content>
              <Group fx:id="scrollContent">
                 <children>
                    <StackPane fx:id="zoomPane" layoutX="-1962.0" layoutY="-859.0" onMouseDragged="#handlePan" onMousePressed="#getCenterPoint" onScroll="#handleZoom" prefHeight="631.0" prefWidth="659.0">
                       <children>
                          <Group fx:id="group" onMouseDragged="#updateLine" onMouseMoved="#updateLine" onMouseReleased="#startDraw">
                             <children>
                                <ImageView fx:id="imageView" fitHeight="424.0" fitWidth="674.0" layoutX="-29.0" layoutY="142.0" pickOnBounds="true" preserveRatio="true">
                                   <image>
                                      <Image url="@../../../../Documents/report.jpg" />
                                   </image></ImageView>
                                <Canvas fx:id="canvas" height="427.0" layoutX="-29.0" layoutY="142.0" width="600.0" />
                             </children>
                          </Group>
                       </children>
                    </StackPane>
                 </children>
              </Group>
           </content>
        </ScrollPane>
        <Button fx:id="loadImage" layoutX="6.0" layoutY="8.0" mnemonicParsing="false" onAction="#loadImage" text="Load Image" AnchorPane.leftAnchor="6.0" />
     </children>
  </AnchorPane>
...