Border-Radius and Shadow on ImageView

Thanks martin for pointing ImagePattern

enter image description here

Rectangle rectangle = new Rectangle(0, 0, 280, 180);
rectangle.setArcWidth(30.0);   // Corner radius
rectangle.setArcHeight(30.0);

ImagePattern pattern = new ImagePattern(
    new Image("file:images/mustang-gt.jpg", 280, 180, false, false) // Resizing
);

rectangle.setFill(pattern);
rectangle.setEffect(new DropShadow(20, Color.BLACK));  // Shadow

Note that here Iam resizing the image to match the size of the rectangle during its load to ensure smoothness.


If you use the answer that jewelsea provided, then make sure to test first whether or not clipping is supported:

Platform.isSupported(ConditionalFeature.SHAPE_CLIP)

I try to avoid conditional features unless I have to use them. In my case, I wanted to make a picture round. So an alternative would be to use a Circle instead of an ImageView:

Circle circle = new Circle(14);
ImagePattern pattern = new ImagePattern(myImage);
circle.setFill(pattern);

The circle can be enhanced to use a shadow if supported:

if (Platform.isSupported(ConditionalFeature.EFFECT)) {
    circle.setEffect(new DropShadow(8, Color.rgb(0, 0, 0, 0.8)));
}

Use the following css to get a drop shadow:

-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);

See the JavaFX CSS Reference guide for details.

To get the border in addition to the drop shadow, place your ImageView containing your Image in a StackPane. And apply the effect css above to the StackPane, in addition to a background and padding on the StackPane.

For example the css below applied to the StackPane containing your ImageView will provide a red border around your image:

-fx-padding: 10;
-fx-background-color: firebrick;

If you want the background defining your border curved at the edges, then use:

-fx-background-radius: 5;

That gets you an image like below where your image is enclosed in a shadowed border:

batmanlost

If you want to actually round the image itself, it's a bit trickier. You need to apply some code to:

  1. Clip the image to a rounded rectangle.
  2. Snapshot the clipped image.
  3. Store the snapshot image back in the ImageView.
  4. Remove the clip from the ImageView.
  5. Apply the drop shadow effect to the ImageView.

Then you can get something like below:

whereisbatman

Some code for that "BatmanLost.java":

import javafx.application.Application;
import javafx.fxml.*;
import javafx.scene.*;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.io.IOException;

public class BatmanLost extends Application {

    class WingClipper {
        @FXML
        private ImageView imageView;

        @FXML
        public void initialize() {
            // set a clip to apply rounded border to the original image.
            Rectangle clip = new Rectangle(
                imageView.getFitWidth(), imageView.getFitHeight()
            );
            clip.setArcWidth(20);
            clip.setArcHeight(20);
            imageView.setClip(clip);

            // snapshot the rounded image.
            SnapshotParameters parameters = new SnapshotParameters();
            parameters.setFill(Color.TRANSPARENT);
            WritableImage image = imageView.snapshot(parameters, null);

            // remove the rounding clip so that our effect can show through.
            imageView.setClip(null);

            // apply a shadow effect.
            imageView.setEffect(new DropShadow(20, Color.BLACK));

            // store the rounded image in the imageView.
            imageView.setImage(image);
        }
    }

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

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource(
                "batmanlostinthemix.fxml"
            )
        );
        loader.setController(new WingClipper());

        Pane batman = loader.load();

        stage.setTitle("Where's Batman?");
        stage.setScene(new Scene(batman));
        stage.show();
    }
}

With some FXML "batmanlostinthemix.fxml":

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

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

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="313.0" prefWidth="477.0" style="-fx-background-color: azure;" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
  <children>
    <ImageView fx:id="imageView" layoutX="29.0" layoutY="44.0" fitHeight="224.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true">
      <image>
        <Image url="http://collider.com/wp-content/uploads/lego-batman-movie-dc-super-heroes-unite-1.jpg" />
      </image>
    </ImageView>
  </children>
</AnchorPane>

Tags:

Javafx