使用 PixelWriter 在 JavaFX Canvas 上进行透明绘图

Posted

技术标签:

【中文标题】使用 PixelWriter 在 JavaFX Canvas 上进行透明绘图【英文标题】:Drawing with transparency on JavaFX Canvas using PixelWriter 【发布时间】:2021-11-23 17:44:37 【问题描述】:

有谁知道为什么使用 drawImage() 在 Canvas 上进行透明绘图工作得非常好,但对 PixelWriter 却完全不工作?我最初认为这可能与 Blend 或画布/上下文上的其他模式/设置有关,但还没有任何运气。

我需要每像素可变的透明度,而不是整个绘制操作的单个透明度值。我将渲染一些“层”(类似于 GIMP 层的工作方式,每个像素具有可选的透明度)。另一个悬而未决的问题是,出于性能原因,我是否最好先将 FINAL 预期输出绘制到 WritableImage,然后再绘制到 Canvas,但这似乎违背了首先使用 Canvas 的意义......

下面是一个示例,它显示了首先将部分透明的颜色绘制到图像上,然后绘制到画布上,然后使用 setColor() 直接绘制到画布上。透明区域是Image draw,不透明区域是setColor部分。我们如何让 setColor() 尊重每个像素的颜色 alpha 透明度?

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;

public class TransparencyTest extends Application 
    
    private static final int width = 800;
    private static final int height = 600;
    private Scene scene;
    private final Canvas canvas = new Canvas(width, height);
    
    @Override
    public void start(Stage stage) 
        scene = new Scene(new Group(canvas));
        stage.setScene(scene);
        render();
        stage.show();
        exitOnEsc();
    
    
    private void render() 
        drawTransparentBg(canvas, 0, 0, width, height);
        
        Color color = Color.web("#77000077");
        
        WritableImage image = new WritableImage(200, 200);
        for (int x = 0; x < 200; x++) 
            for (int y = 0; y < 200; y++) 
                image.getPixelWriter().setColor(x, y, color);
            
        
        canvas.getGraphicsContext2D().drawImage(image, 50, 50);
        
        for (int x = 0; x < 50; x++) 
            for (int y = 0; y < 50; y++) 
                canvas.getGraphicsContext2D().getPixelWriter().setColor(x, y, color);
            
        
    
    
    public void drawTransparentBg(Canvas canvas, int xPos, int yPos, int width, int height) 
        int gridSize = 8;
        boolean darkX = true;
        String darkCol = "#111111";
        String lightCol = "#222266";
        
        for (int x = xPos; x < canvas.getWidth(); x += gridSize) 
            boolean dark = darkX;
            darkX = !darkX;
            if (x > width) 
                break;
            
            
            for (int y = yPos; y < canvas.getHeight(); y += gridSize) 
                if (y > height) 
                    break;
                
                
                dark = !dark;
                String color;
                if (dark) 
                    color = darkCol;
                 else 
                    color = lightCol;
                
                canvas.getGraphicsContext2D().setFill(Paint.valueOf(color));
                canvas.getGraphicsContext2D().fillRect(x, y, gridSize, gridSize);
            
        
    
    
    private void exitOnEsc() 
        scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> 
            if (event.getCode().equals(KeyCode.ESCAPE)) 
                Platform.exit();
            
        );
    

【问题讨论】:

您查看过Oracle canvas tutorial 吗?标题为“与用户交互”的部分,使用多个画布和透明度创建了一个简单的(可能为了您的目的而简单化)图层系统。但它不使用 PixelWriter,因此该信息是解决您的问题的替代方法(可能不适用于您的应用),而不是您问题的答案。 【参考方案1】:

GraphicsContext 以默认的BlendMode 开头,所有形式的drawImage() 都使用当前模式。相反,PixelWriter 方法替换 值,忽略BlendMode

下面的示例让您试验支持的BlendMode 值以查看效果。相关示例见here 和here。

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ChoiceBox;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class TransparencyTest extends Application 

    private static final int S = 8;
    private static final int W = S * 36;
    private static final int H = W;
    private final Canvas canvas = new Canvas(W, H);
    private final GraphicsContext g = canvas.getGraphicsContext2D();
    private BlendMode mode = g.getGlobalBlendMode();

    @Override
    public void start(Stage stage) 
        render(mode);
        BorderPane root = new BorderPane(new Pane(canvas));
        ObservableList<BlendMode> modes
            = FXCollections.observableArrayList(BlendMode.values());
        ChoiceBox<BlendMode> cb = new ChoiceBox<>(modes);
        cb.setValue(mode);
        cb.valueProperty().addListener((o) -> 
            render(cb.getValue());
        );
        root.setBottom(cb);
        stage.setScene(new Scene(root));
        stage.show();
    

    private void render(BlendMode mode) 
        drawBackground();
        g.setGlobalBlendMode(mode);
        Color color = Color.web("#7f00007f");
        int s = 24 * 8;
        WritableImage image = new WritableImage(s, s);
        for (int x = 0; x < s; x++) 
            for (int y = 0; y < s; y++) 
                image.getPixelWriter().setColor(x, y, color);
            
        
        s = 6 * 8;
        g.drawImage(image, s, s);
        for (int x = 0; x < s; x++) 
            for (int y = 0; y < s; y++) 
                g.getPixelWriter().setColor(x, y, color);
            
        
    

    public void drawBackground() 
        g.setGlobalBlendMode(BlendMode.SRC_OVER);
        Color darkCol = Color.web("#333333");
        Color lightCol = Color.web("#cccccc");
        boolean dark = false;
        for (int x = 0; x < W; x += S) 
            dark = !dark;
            for (int y = 0; y < H; y += S) 
                dark = !dark;
                g.setFill(dark ? darkCol : lightCol);
                g.fillRect(x, y, S, S);
            
        
    

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

【讨论】:

以上是关于使用 PixelWriter 在 JavaFX Canvas 上进行透明绘图的主要内容,如果未能解决你的问题,请参考以下文章

将 JavaFX 与 Intellij IDEA 结合使用

javafx媒体可以播放mp4格式的视频文件吗?

如何在javafx中将Optional Double转换为Double

如何更改javaFX中按钮的图像?

javafx已经没多少用了

JavaFx:JavaFx使用beetl模板,解决“找不到指定模板或者加载模板错(TEMPLATE_LOAD_ERROR)”问题