Question

I try to add emboss effect (also called bump mapped) to a complex clipped image. (Namly a puzzle piece.) This could be done by a per pixel operation, but it is very expensive: the exact form of the clipping is runtime determined, so even the bump map would have to be generated runtime.

The lighting effect of JavaFX offers a similar effect out-of-box:

// Create clipping
pieceClip = createPiece(topEdge, leftEdge, bottomEdge, rightEdge);
pieceClip.setFill(Color.WHITE);
pieceClip.setStroke(null);

// Create clipped image view
imageView.setImage(image);
imageView.setClip(pieceClip);

// Create ambient light
Light.Distant light = new Light.Distant();
light.setAzimuth(-135.0);

// Create lighting effect
Lighting lighting = new Lighting();
lighting.setLight(light);
lighting.setSurfaceScale(4.0);

// Create shape to apply lighting on (the shape is the same as the clipping)
Shape pieceStroke1 = createPiece(topEdge, leftEdge, bottomEdge, rightEdge);
pieceStroke1.setFill(new Color(1, 1, 1, 0.4));
pieceStroke1.setStroke(null);

// Apply lighting
pieceStroke1.setEffect(lighting);

// Add clipped image and lighting overlay to the shape
getChildren().addAll(imageView, pieceStroke1);

This works almost well, the lighting effect:

However, it has a side effect: due to the overlay being white and only semi-transparent, it dims the image (makes its a littly foggy, much less vivid as it was).

I tried to play with Blend (COLOR_BURN, DARKEN, etc.) to restore the image colors, but being not quite at home with these effects, I failed.

How could I reserve my image color saturation, but applying the emboss effect?

I have several idea how to do it, but I have no idea which one would work:

  1. Applying lighting directly to the image. It failed, because the lighting is applied to the unclipped image.
  2. Correcting the color saturation reduction by applying Blend effect on either the image or the lighting overlay It failed due to my incompetence in these effects.
  3. Clipping the overlay shape to cover only the lighting affected area It would need an offsetting of bezier curved which are hard and expensive to do.
  4. Alterring the overlay shape alpha to follow the lighting effect intensity.

Any idea would be deeply appreciated.

Was it helpful?

Solution

Solution

Apply a ColorAdjust effect to the Lighting effect in an effect chain.

The lighting effect is going to change the appearance and color of the image to some extent no matter what you do (because that is what lighting does), but by applying the additional color adjustment, you can get an image which is recognizably pretty close to the original coloring.

If you need to adjust the shape of the image, snapshot the clipped image and apply the effect to the snapshot if that is what you want, as explained in the answer to: Border-Radius and Shadow on ImageView.

Example

// increase brightness and contrast.
ColorAdjust brightLight = new ColorAdjust(0, 0, .25, 0.25); 

// chain in your lighting effect.
brightLight.setInput(lighting);

// apply the chained effect to your image.
ImageView litAdjusted = new ImageView(image);
litAdjusted.setEffect(brightLight);

Executable sample

herebedragons

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;

// Java 8 code
public class HereBeDragons extends Application {

    @Override public void start(final Stage stage) {
        Image image     = new Image(imageLoc);
        Image clipImage = new Image(CLIP_IMAGE_LOC);

        ImageView plain = new ImageView(image);

        ImageView lit = new ImageView(image);
        Lighting lighting = createLighting();
        lit.setEffect(lighting);

        ImageView litAdjusted = new ImageView(image);
        ColorAdjust brightLight = new ColorAdjust(0, 0, .25, 0.25);
        brightLight.setInput(lighting);
        litAdjusted.setEffect(brightLight);

        plain.setClip(new ImageView(clipImage));
        SnapshotParameters params = new SnapshotParameters();
        params.setFill(Color.TRANSPARENT);
        WritableImage clippedImage = plain.snapshot(params, null);
        ImageView litAdjustedClip = new ImageView(clippedImage);
        litAdjustedClip.setEffect(brightLight);
        plain.setClip(null);

        HBox layout = new HBox(
                10,
                new CaptionedImage(plain,       "Plain"),
                new CaptionedImage(lit,         "Lit"),
                new CaptionedImage(litAdjusted, "Lit and Adjusted"),
                new CaptionedImage(litAdjustedClip, "Clipped,\nLit and Adjusted")
        );
        layout.setPadding(new Insets(20));
        layout.setStyle("-fx-background-color: lightblue;");

        stage.setTitle("Here be Dragons");
        stage.setScene(new Scene(layout, Color.LIGHTBLUE));
        stage.setResizable(false);
        stage.show();
    }

    private Lighting createLighting() {
        // Create ambient light
        Light.Distant light = new Light.Distant();
        light.setAzimuth(-135.0);

        // Create lighting effect
        Lighting lighting = new Lighting();
        lighting.setLight(light);
        lighting.setSurfaceScale(4.0);

        return lighting;
    }

    private class CaptionedImage extends Label {
        public CaptionedImage(ImageView imageView, String caption) {
            setText(caption);
            setGraphic(imageView);
            setContentDisplay(ContentDisplay.TOP);
            setTextAlignment(TextAlignment.CENTER);

            setStyle(
                    "-fx-text-fill: midnightblue; " +
                    "-fx-font-size: 16px; " +
                    "-fx-font-family: palatino; " +
                    "-fx-font-style: italic;"
            );
        }
    }

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

    private static final String imageLoc =
        "http://icons.iconarchive.com/icons/custom-icon-design/round-world-flags/128/Wales-icon.png";    
    // icon license: Free for non-commercial use. 
    // Buy commercial license here: 
    // http://www.customicondesign.com/free-icons/flag-icon-set/flat-round-world-flag-icon-set

    private static final String CLIP_IMAGE_LOC =
            "http://icons.iconarchive.com/icons/gpritiranjan/simple-christmas/128/star-icon.png";
    // star icon license: freeware, commercial usage allowed.
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top