dajood
dajood

Reputation: 4050

How to apply a blur only to a rectangular area within a pane?

Let's say I've got a StackPane which has a BackgroundImage as background and another StackPane (or another component, if neccessary) as a child. The child covers only a part of the parent StackPane.

I'd like to know how to apply a GaussianBlur just to the area the child covers, so that the BackgroundImageis blurry in this area.

The size of the child changes when the parent is resized. It would be perfect to get a solution that will resize just in time, too.

Upvotes: 1

Views: 550

Answers (1)

Roland
Roland

Reputation: 18415

If you want to do it manually, you can use the snapshot function to create a snapshot image, blur it and apply it to the child every time the parent is resized.

However, invoking snapshot all the time will cause performance loss. I rather suggest you create 2 images, one normal and one blurred, and display a viewport of the blurred one.

Here's a more "complex" example with a circle where the viewport isn't sufficient. The clip method is used in this case:

public class Lens extends Application {

    Image image = new Image( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");

    CirclePane circlePane;

    @Override
    public void start(Stage primaryStage) {

        ImageView normalImageView = new ImageView( image);
        ImageView blurredImageView = new ImageView( image);
        blurredImageView.setEffect(new GaussianBlur( 40));

        Group root = new Group();

        root.getChildren().addAll( normalImageView);

        Scene scene = new Scene( root, 1024, 768);

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

        // pane with clipped area
        circlePane = new CirclePane( blurredImageView);
        makeDraggable( circlePane);

        root.getChildren().addAll( circlePane);

    }

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


    private class CirclePane extends Pane {

        ImageView blurredImageView;
        ImageView clippedView = new ImageView();

        public CirclePane( ImageView blurredImageView) {

            this.blurredImageView = blurredImageView;

            // new imageview
            update();

            getChildren().addAll( clippedView);

        }

        public void update() {

            // create circle
            Circle circle = new Circle( 200);
            circle.relocate( getLayoutX(), getLayoutY());

            // clip image by circle
            blurredImageView.setClip(circle);

            // non-clip area should be transparent
            SnapshotParameters parameters = new SnapshotParameters();
            parameters.setFill(Color.TRANSPARENT); 

            // new image from clipped image
            WritableImage wim = null;
            wim = blurredImageView.snapshot(parameters, wim);

            clippedView.setImage( wim);

        }

    }

    // make node draggable
    class DragContext { 
        double x;
        double y; 
    } 

    public void makeDraggable( Node node) {

        final DragContext dragDelta = new DragContext();

        node.setOnMousePressed(mouseEvent -> {

            dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
            dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();

        });

        node.setOnMouseDragged(mouseEvent -> {

            node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y);
            circlePane.update();

        });

    }

}

Just click on the circle and drag it around.

enter image description here

Upvotes: 4

Related Questions