Home > Software engineering >  Can not get the right color when hovering over image node in javafx
Can not get the right color when hovering over image node in javafx

Time:10-09

When I hover over the Image I am getting the wrong color value in some pixels of the image, which seems to be a kind of pixel shift.

I am using a MouseMoved event on the ImageView to get the x and y coordinates of the cursor on the image, then I create a Color instance using the color from those coordinates. Finally, I use that color to fill a Circle node.

I would like to know why it is returning the wrong color value for some pixels. PS: I tried that on png and jpeg images but the same thing is happening.

This is the initialize method of the controller where I am setting the event listener to the ImageView:

 @FXML
    public ImageView inputImage;

    public void initialize() {

        inputImage.setOnMouseMoved(event -> {
            int xPosition = (int) event.getX();
            int yPosition = (int) event.getY();

            Color pixelColor = inputImage.getImage().getPixelReader().getColor(xPosition, yPosition);

            circleColor.setFill(pixelColor);
        });
    }

A part of the FXML file :

            <VBox HBox.hgrow="ALWAYS" alignment="CENTER">
                <HBox alignment="CENTER">
                    <VBox.margin>
                        <Insets bottom="50.0"/>
                    </VBox.margin>

                    <ImageView fx:id="inputImage" pickOnBounds="true" preserveRatio="true" fitHeight="300"
                               fitWidth="300">
                        <Image url="@../Resources/Default-image.png" backgroundLoading="true"/>
                    </ImageView>
                </HBox>

                <HBox alignment="CENTER">
                    <Button onAction="#handleFileChooserClick" text="load image"/>
                </HBox>
            </VBox>

            <VBox prefWidth="200" alignment="TOP_CENTER">
                <padding>
                    <Insets top="60" bottom="60"/>
                </padding>

                <VBox alignment="TOP_CENTER">
                    <Circle radius="70" fill="orange" stroke="black" fx:id="circleColor"/>
                    <VBox.margin>
                        <Insets bottom="50.0"/>
                    </VBox.margin>
                </VBox>

                <Button textAlignment="CENTER" text="Isolate Color"/>
            </VBox>

This how it looks like : enter image description here

CodePudding user response:

you're explicitly setting the fitwidth / fitheight through fxml so the display resolution may differ from the original image resolution if the image dimensions are not equal to the manually set fit width and height.

what you need to do is convert the mouse position x and y accordingly from the display range of [0...displayWidth] and [0...displayHeight] to the original dimensions [0...originalWidth] [0...originalHeight].

now the tricky part is that when you use preserveRatio, the display dimensions are calculated and are not always equal to the fit width and height, example

import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

import java.util.Objects;

public class ColorPickerApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Circle swatch = new Circle(30);

        ImageView imageView =
                new ImageView(
                        new Image(
                                Objects.requireNonNull(
                                        ColorPickerApp.class.getResource(
                                                "Dragon-icon.png"
                                        )
                                ).toExternalForm()
                        )
                );
        imageView.setPickOnBounds(true);

        imageView.setOnMouseMoved(event -> {
            int xPosition = (int) event.getX();
            int yPosition = (int) event.getY();

            Color pixelColor = imageView.getImage().getPixelReader().getColor(xPosition, yPosition);
            swatch.setFill(pixelColor);
            System.out.println(pixelColor);
        });

        VBox layout = new VBox(10, swatch, imageView);
        layout.setAlignment(Pos.CENTER);
        layout.setPadding(new Insets(10));


        stage.setScene(new Scene(layout));
        stage.show();
    }

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

The dragon image, Dragon-icon.png, in the sample:

Dragon-icon.png

I set pickOnBounds to true so that the transparent parts of the image will register as color changes when moused over.

If you move the mouse really rapidly, results may not be exact because the polling of the mouse info isn't immediate, but if you move the mouse slowly, it should work fine, at least from my testing.

  • Related