我正在 JavaFX 中开发一个 gui,它需要在画布上合成大量对象(通常使用 alpha 掩码等)。
为了在 HTML5 画布上进行比较,这可以通过
drawImage
函数在 DOM 结构外部的临时画布对象的帮助下轻松完成。例如,要使用 alpha 蒙版在画布上绘制图像,我首先在临时画布上绘制图像,使用 globalCompositeOperation = "destination-in"
在其上绘制(即位图传输)蒙版,然后使用复合模式在原始画布上绘制临时画布source-over
。每个此类操作都可以重复使用临时画布。简单易行。
但是,据我所知,到目前为止,在 JavaFX 中执行此操作的推荐方法是使用分组层,即永远不会“展平”的多个重叠画布节点。
我也可以在 HTML5 中这样做,但在我最近的项目中,这会导致数十或数百个可见层,这显然是非常愚蠢的。我的方法给了我出色的表现。
话虽如此,是否有一种合理的方法可以在 JavaFX 画布上执行相同的操作?我认为手动执行逐像素复制是笨拙的最后手段。
我错过了什么?我是否以错误的方式思考 JavaFX?
我之前在 JavaFx 和 Android 上这样做过,我不知道他们在 HTML 5 上这样做
所以无论如何
你可以像在 HTML 5 上一样做你可以创建所谓的
mainCanvas
画布包含另一个画布的完成版本让我们说 tempCanvas
在临时画布中你可以画任何你想要的东西并应用你想要的遮罩然后你也拍摄画布的快照(因为 Canvas
是 Node
你可以使用此代码拍摄它的快照)
WritableImage writableImage = new WritableImage(mainCanvas.getWidth(), mainCanvas.getHeight());
tempCanvas.snapshot(null, writableImage);
GraphicsContext context = mainCanvas.getGraphicsContext2D();
context.drawImage(writableImage,mainCanvas.getWidth(), mainCanvas.getHeight());
这是老问题,可能对将来的人有用。
public static Image layeredImageNotInFXThread(List<Image> images){
if(images == null || images.isEmpty()){
return null;
}
int w = 0, h = 0;
List<BufferedImage> bufferedImages = new ArrayList<>();
for(Image pImage : images){
BufferedImage bImage = SwingFXUtils.fromFXImage(pImage, null);
bufferedImages.add(bImage);
// create the new image, canvas size is the max. of both image sizes
w = Math.max(w, bImage.getWidth());
h = Math.max(h, bImage.getHeight());
}
BufferedImage bufferedImage = layeredBufferedImage(bufferedImages, w, h);
Image image = SwingFXUtils.toFXImage(bufferedImage, null);
return image;
}
/**
* Must be run Java FX tThread
* @param images
* @return
*/
public static Image layeredImage(List<Image> images){
if(images == null || images.isEmpty()){
return null;
}
Image image = null;
Canvas canvas = new Canvas(images.get(0).getWidth(), images.get(0).getHeight());
//WritableImage writableImage = new WritableImage((int)images.get(0).getWidth(), (int)images.get(0).getHeight());
//writableImage.getPixelWriter();
// writableImage
//canvas.snapshot(null, writableImage);
GraphicsContext gc = canvas.getGraphicsContext2D();
for(Image pImage : images){
gc.drawImage(pImage, 0, 0);
}
SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.TRANSPARENT);
image = canvas.snapshot(snapshotParameters, null);
return image;
}
public static BufferedImage layeredBufferedImage(List<BufferedImage> images, int width, int height){
//BufferedImage bImage = SwingFXUtils.fromFXImage(view.getImage(), null);
if(images == null || images.isEmpty()){
return null;
}
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// paint both images, preserving the alpha channels
Graphics gc = bufferedImage.getGraphics();
for(BufferedImage pImage : images){
gc.drawImage(pImage, 0, 0, null);
}
gc.dispose();
// Save as new image
//ImageIO.write(bufferedImage, "PNG", new File(path, "bufferedImage.png"));
return bufferedImage;
}
public static void main(String[] args) {
new JFXPanel(); // this will prepare JavaFX toolkit and environment
Image image1 = new Image("/icons/drone.png");
Image image2 = new Image("/icons/icons8_lock_100px.png");
List<Image> images = new ArrayList<>();
images.add(image1);
images.add(image2);
//Image layeredImage = layeredImage(images);
Image layeredImage = layeredImageNotInFXThread(images);
// Write snapshot to file system as a .png image
File outFile = new File("D://tmp//layeredImage.png");
try {
ImageIO.write(SwingFXUtils.fromFXImage(layeredImage, null),
"png", outFile);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}