1 Reply Latest reply: Feb 12, 2013 8:54 PM by jsmith RSS

    Effects on effects

    svanimpe
      This may be a metaphysical question, but is there a way to set effects on effects ?

      I was playing around with Reflection and was trying to created a blurred reflection, but this does not seem to be as straightforward as I might have thought.

      Chaining effects together using setInput did not work for me because I only want to blur the reflection, not the node.
        • 1. Re: Effects on effects
          jsmith
          In general setInput is the way to go to apply effects on effects, but in your case you want to apply an effect on only a partial area of the effected node, so setInput doesn't really work.

          The code below will work in some limited cases.

          This solution is probably not what you are really looking for because it is a bit clunky in implementation and limited in it's possible uses, but I don't know of a better way to accomplish the effect you desire short of creating your own effect - e.g. An EffectClip effect which limits the area that subsequent effects apply to.

          Custom effects might be realizable now that more of the JavaFX code has been open sourced.

          General strategy is:
          // construct a reflected label to be displayed in the scene.
          // show the reflected label on the stage.
          // snapshot the reflected part of the label to an image.
          // blur the reflected part of the label.
          // place the original label (now without it's original reflection)
          // in a VBox together with it's blurred reflection.
          // replace the original reflected label with the blurred reflected version.
          import javafx.application.Application;
          import javafx.geometry.Bounds;
          import javafx.geometry.Rectangle2D;
          import javafx.scene.Scene;
          import javafx.scene.SnapshotParameters;
          import javafx.scene.control.Label;
          import javafx.scene.effect.BoxBlur;
          import javafx.scene.effect.Reflection;
          import javafx.scene.image.ImageView;
          import javafx.scene.image.WritableImage;
          import javafx.scene.layout.StackPane;
          import javafx.scene.layout.VBox;
          import javafx.stage.Stage;
          
          public class InReflection extends Application {
            @Override public void start(Stage stage) {
              // construct a reflected label to be displayed in the scene.
              Label label = new Label("The mirror crack'd from side to side");
              label.setStyle("-fx-font-size: 40px; -fx-font-style: italic;");
              label.setEffect(new Reflection());
              
              // show the reflected label on the stage
              StackPane layout = new StackPane();
              layout.getChildren().setAll(label);
              stage.setScene(new Scene(layout, 800, 200));
              stage.show();
              
              // snapshot the reflected part of the label to an image.
              SnapshotParameters params = new SnapshotParameters();
              Bounds effectiveBounds = label.getBoundsInParent();
              params.setViewport(new Rectangle2D(effectiveBounds.getMinX(), effectiveBounds.getMinY() + effectiveBounds.getHeight() * 2 / 3, effectiveBounds.getWidth(), effectiveBounds.getHeight() * 1 /3));
              WritableImage image = label.snapshot(params, null);
          
              // blur the reflected part of the label.
              ImageView reflectedView = new ImageView(image);
              reflectedView.setEffect(new BoxBlur());
              
              // place the original label (now without it's original reflection)
              // in a VBox together with it's blurred reflection.
              VBox blurReflectedLabel = new VBox();
              label.setEffect(null);
              blurReflectedLabel.getChildren().setAll(label, reflectedView);
              blurReflectedLabel.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
              blurReflectedLabel.setPrefSize(effectiveBounds.getWidth(), effectiveBounds.getHeight());
              blurReflectedLabel.setMaxSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
              
              // replace the original reflected label with the blurred reflected version.
              layout.getChildren().setAll(blurReflectedLabel);
            }
          
            public static void main(String[] args) { launch(args); }
          }