将反射应用于JavaFX中的DropShadow效果

时间:2016-08-04 15:04:30

标签: java javafx

使用JavaFX,我试图将文本绘制到画布上,并将阴影和反射效果链接在一起。

以下代码将显示反射的红色文本,然后将阴影应用于原始文本和反射文本。

Canvas canvas = new Canvas(400,400);
GraphicsContext context = canvas.getGraphicsContext2D();
context.setFont( new Font("Arial Bold", 48) );
context.setFill(Color.RED);
DropShadow shadow = new DropShadow(6, 2, 2, Color.BLACK);
Reflection reflect = new Reflection(10, 1.0, 1.0, 0.0);
shadow.setInput(reflect);
context.setEffect(shadow);
context.fillText("Hello, world!", 100,100);

但是,阴影在反射中显示为“向后”,因为需要首先应用阴影才能获得逼真的效果。我尝试通过更改效果的应用顺序来完成此操作,方法是更改​​上面的setInputsetEffect代码行,如下所示:

reflect.setInput(shadow);
context.setEffect(reflect);

然而,结果是只应用了反射;我根本看不到任何投影。

为什么不应用/不显示投影?

如何重写此代码以达到预期效果(如果可能,仅使用效果组合)?

1 个答案:

答案 0 :(得分:1)

我不知道使用标准API使用GraphicsContext是否可以实现您所说的内容,因此欢迎其他答案。但是,这可能是一个临时的解决方法,只要它实际上是您需要的。首先应用阴影,然后逐个像素地复制图像以模仿反射效果。请看下面的截图:(原件 - 左侧,新的 - 右侧)。

Reflection

下面附有完整的工作示例。它需要一些调整来获得一般解决方案,但应该有足够的开始。

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class FXApp extends Application {

    private Parent createContent() {
        Canvas canvas = new Canvas(400,400);

        Font font = new Font("Arial Bold", 48);
        Color fill = Color.RED;
        DropShadow shadow = new DropShadow(6, 2, 2, Color.BLACK);

        fillTextWithReflection(canvas.getGraphicsContext2D(), "Hello, world!", 100, 100, font, fill, shadow);

        return new Pane(canvas);
    }

    private void fillTextWithReflection(GraphicsContext g, String text, double textX, double textY, Font font, Color fill, Effect effect) {
        Text t = new Text(text);
        t.setFont(font);

        // 5 px margin
        Canvas tmpCanvas = new Canvas(t.getLayoutBounds().getWidth() + 5, t.getLayoutBounds().getHeight() + 5);

        // set configuration
        GraphicsContext tmpContext = tmpCanvas.getGraphicsContext2D();
        tmpContext.setFont(font);
        tmpContext.setFill(fill);
        tmpContext.setEffect(effect);

        // draw on temporary context
        tmpContext.fillText(text, 0, font.getSize());

        // take a snapshot of the text
        WritableImage snapshot = tmpCanvas.snapshot(null, null);

        int w = (int)snapshot.getWidth();
        int h = (int)snapshot.getHeight();

        WritableImage reflected = new WritableImage(w, h);

        // make an 'inverted' copy
        for (int y = 0; y < h; y++) {
            // imitate fading out of reflection
            double alpha = y / (h - 1.0);

            for (int x = 0; x < w; x++) {
                Color oldColor = snapshot.getPixelReader().getColor(x, y);
                Color newColor = Color.color(oldColor.getRed(), oldColor.getGreen(), oldColor.getBlue(), alpha);

                reflected.getPixelWriter().setColor(x, h - 1 - y, newColor);
            }
        }

        // draw on the actual context
        // images are drawn from x, y top-left but text is filled from x, y + h
        // hence corrections
        // this can be replaced with actual fillText() call if required
        g.drawImage(snapshot, textX, textY - font.getSize());
        g.drawImage(reflected, textX, textY + h - font.getSize());
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.show();
    }
}