Search code examples
javajavafxhoverzoomingcrop

Javafx Image Crop according to hovering mouse position


I am developing a tracing program by placing dots to anatomical points on Xray. My problem is, I'm trying to zoom into my X-ray image (Imageview) while my mouse hovering onto it and displaying in another Imageview. I want my mouse to create a virtual rectangle around it and stays at the center while moving and transfers that live image to other small Imageview, I've done this so far but there are some glitches. My rectangle does not follow my mouse exactly as it should, and the glitch accelerates when I move into corners, What can you suggest? I'm sharing my error and my codes to clarify all those things.

Maybe I should create a rectangle shape and place it around mouse position and crop the image live, fit into the zoomedImageview, and display all the time, but I am stuck in this.

The main problem is, mouse position and center of zoomedImageview's center position has to coincide all the time.

enter image description here

Xray Imageview Fitwidth-height is 800x800 zoomedImageview Fitwidth-height is 250x250

public class DrawingController {

        @FXML 
    ImageView xrayImage;
        
         @FXML
    ImageView zoomedImage;

   public void initialize()
    {

   xrayImage.setOnMouseMoved(e -> {    //HOVERING ON XRAY
   
PixelReader reader = XrayImageview.getImage().getPixelReader();   
WritableImage newImage = new WritableImage(reader,

(int) e.getX(),  //x and y is picked according to hovering mouse position
   (int) e.getY(),
   (int) 250,     // 250x250 rectangle is requested.
   (int) 250);

zoomedImage.setImage(newImage);  // Displaying zoomed image

        });
}}

And My FXML file is:

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

<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="913.0" prefWidth="1238.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.java.controllers.DrawingController">
   <children>
      <ImageView fitHeight="800.0" fitWidth="800.0" layoutX="418.0" layoutY="33.0" pickOnBounds="true" preserveRatio="true" fx:id="xrayImage">
         <image>
            <Image url="@threatCare.png" />
         </image>
      </ImageView>
      <ImageView fx:id="zoomedImage" fitHeight="250.0" fitWidth="250.0" layoutX="108.0" layoutY="433.0" pickOnBounds="true" preserveRatio="true" />
   </children>
</AnchorPane>

And the Main class :

package main.java;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import main.java.database.DatabaseCreator;

import javax.swing.text.html.ImageView;
import java.awt.*;

public class Runner extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception{
     
        Parent root = FXMLLoader.load(getClass().getResource("../resources/DrawingPanel.fxml"));

        primaryStage.setTitle("TreatCare Software Systems");
         Scene scene = new Scene(root,1200,950);
        primaryStage.setScene(scene);
        primaryStage.setMinWidth(1200);
        primaryStage.setMinHeight(950);
        primaryStage.setMaximized(true);
        primaryStage.setOnCloseRequest( e -> SystemClose());
        primaryStage.show();
    }

    private void SystemClose(){
        System.exit(0);         
    }

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

Solution

  • You are scaling your image, so you need to convert your mouse coordinates according to that scaling.

    From your .fxml file:

    <ImageView fitHeight="800.0" fitWidth="800.0" layoutX="418.0" layoutY="33.0" pickOnBounds="true" preserveRatio="true" fx:id="xrayImage">
    

    There are three attributes in that line which scale the image:

    • fitWidth="800.0" - scales image to 800 pixels wide
    • fitHeight="800.0" - scales image to 800 pixels high
    • preserveRatio="true" - overrides all scaling so image always has the same scale in both dimensions

    So you need to multiply your mouse coordinates according to those:

    xrayImage.setOnMouseMoved(e -> {    //HOVERING ON XRAY
        Image image = xrayImage.getImage();
    
        double x = e.getX();
        double y = e.getY();
        double xScale = (xrayImage.getFitWidth() > 0 ?
            image.getWidth() / xrayImage.getFitWidth() : 1);
        double yScale = (xrayImage.getFitHeight() > 0 ?
            image.getHeight() / xrayImage.getFitHeight() : 1);
        if (xrayImage.isPreserveRatio()) {
            double scale = Math.max(xScale, yScale);
            x *= scale;
            y *= scale;
        } else {
            x *= xScale;
            y *= yScale;
        }
    
        x = Math.max(0, x - 125);
        y = Math.max(0, y - 125);
        double width = Math.min(image.getWidth() - x, 250);
        double height = Math.min(image.getHeight() - y, 250);
    
        PixelReader reader = image.getPixelReader();   
        WritableImage newImage = new WritableImage(reader,
            (int) x, (int) y, (int) width, (int) height);
    
        zoomedImage.setImage(newImage);  // Displaying zoomed image
    });