Это еще один вопрос о масштабировании и панорамировании, но прежде чем отклонить это, пожалуйста, уделите время прочтению.
Я создаю простое приложение для рисования, в котором мне нужно рисовать поверх изображения. И когда я увеличиваю / уменьшаю чертеж, остаюсь на изображении, где он был нарисован. Я использую 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>