在 javafx Canvas/GraphicsContext 中使用透明颜色覆盖路径
Override path with transparent color in javafx Canvas/GraphicsContext
我想知道 JavaFX 中 Canvas 的 GraphicsContext 中是否有任何方法可以覆盖路径中的所有像素,使它们再次变得透明。类似于 clearRect 函数,但适用于任何路径。
我查看了不同的 BlendModes 和 Effects,但似乎没有办法实际覆盖 alpha 值,例如
- 使用 Porter/Duff Src 运算符呈现透明颜色
- 在 HTML5
中使用 globalCompositeOperation destination-out 渲染非透明颜色
如果有人知道无需镶嵌路径和使用多个 clearRect 调用即可实现此目的的方法,我将非常高兴。谢谢。
http://docs.oracle.com/javase/8/javafx/api/javafx/scene/canvas/GraphicsContext.html
针对第一条评论补充:
我没有使用 Path 节点,而是使用 Canvas 节点来使用 GraphicsContext 执行渲染,因此无法修改先前渲染路径的颜色。
我正在尝试做类似
的事情
Canvas canvas = …;
final GraphicsContext context = canvas.getGraphicsContext();
context.setFill(Color.BLUE);
context.fillRect(0,0,100,100);
…
…
稍后我想将路径中的像素重置为透明
context.setStroke(Color.TRANSPARENT);
context.beginPath();
context.moveTo(0, 0);
context.lineTo(10,10);
…
context.stroke();
但是,当应用混合时,这不起作用。在其他渲染库中,可以使用例如上面提到的一种混合/合成方法可以实现这一点,但直到现在我还无法在 javafx 中识别出这样的功能。
我还没有找到一种机制来根据路径清除 canvas。但您可以手动执行此操作:
- 创建一个蒙版,在上面绘制路径
- 手动混合图像和蒙版
应用了带有路径的遮罩的蓝色矩形示例:
public class ManualBlendWithMask extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Group root = new Group();
// create canvas
Canvas canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
// create canvas with blue rectangle
GraphicsContext canvasGC = canvas.getGraphicsContext2D();
canvasGC.setFill(Color.BLUE);
canvasGC.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
// create mask
Canvas mask = new Canvas( canvas.getWidth(), canvas.getHeight());
GraphicsContext maskGC = mask.getGraphicsContext2D();
maskGC.setFill(Color.WHITE);
maskGC.fillRect(0, 0, mask.getWidth(), mask.getHeight());
// draw path
maskGC.setStroke(Color.BLACK);
maskGC.setLineWidth(20);
maskGC.beginPath();
maskGC.moveTo(0, 0);
maskGC.lineTo(400, 400);
maskGC.stroke();
// get image from canvas
WritableImage srcImage = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
srcImage = canvas.snapshot(null, srcImage);
// get image from mask
WritableImage srcMask = new WritableImage((int) mask.getWidth(), (int) mask.getHeight());
srcMask = mask.snapshot(null, srcMask);
PixelReader maskReader = srcMask.getPixelReader();
PixelReader imageReader = srcImage.getPixelReader();
int width = (int) srcMask.getWidth();
int height = (int) srcMask.getHeight();
// create dest image
WritableImage dest = new WritableImage(width, height);
PixelWriter writer = dest.getPixelWriter();
// blend image and mask
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Color maskColor = maskReader.getColor(x, y);
Color imageColor = imageReader.getColor(x, y);
if (maskColor.equals(Color.WHITE)) {
writer.setColor(x, y, imageColor);
}
}
}
// clear canvas and fill it with the blended image
canvasGC = canvas.getGraphicsContext2D();
canvasGC.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
canvasGC.drawImage(dest, 0, 0);
// draw background with gradient
Rectangle rect = new Rectangle(400, 400);
rect.setFill(new LinearGradient(0, 0, 1, 1, true, CycleMethod.REFLECT, new Stop(0, Color.RED), new Stop(1, Color.YELLOW)));
// show nodes
root.getChildren().addAll(rect, canvas);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
当然,如果有人能想出一个使用 BlendModes 的例子就更好了。
在看到 2015 年的几篇帖子之后,我发现这仍然没有被添加到 javaFX
中,这让我很惊讶。据我所知,AWT
中有一个 "DST-OUT" 混合选项,那么为什么 javaFX
中没有?!
无论如何..我在找出自己的解决方案后回到这里,所以我可以分享我的解决方案,并希望能帮助下一个来这里寻找答案的不安分的灵魂。
我的具体需要是在基于 SVGPath
的区域中使 canvas 透明。所以这是我的解决方案:
private void clearPath(GraphicsContext gc, SVGPath path) {
int xstart = (int) path.getLayoutX();
int xend = (int) (xstart + path.getLayoutBounds().getMaxX());
int ystart = (int) path.getLayoutY();
int yend = (int) (ystart + path.getLayoutBounds().getMaxY());
PixelWriter pw = gc.getPixelWriter();
for (int x = xstart; x <= xend; x++) {
for (int y = ystart; y <= yend; y++) {
if(path.contains(new Point2D(x, y))) {
pw.setColor(x, y, Color.TRANSPARENT);
}
}
}
}
我想知道 JavaFX 中 Canvas 的 GraphicsContext 中是否有任何方法可以覆盖路径中的所有像素,使它们再次变得透明。类似于 clearRect 函数,但适用于任何路径。
我查看了不同的 BlendModes 和 Effects,但似乎没有办法实际覆盖 alpha 值,例如
- 使用 Porter/Duff Src 运算符呈现透明颜色
- 在 HTML5 中使用 globalCompositeOperation destination-out 渲染非透明颜色
如果有人知道无需镶嵌路径和使用多个 clearRect 调用即可实现此目的的方法,我将非常高兴。谢谢。
http://docs.oracle.com/javase/8/javafx/api/javafx/scene/canvas/GraphicsContext.html
针对第一条评论补充: 我没有使用 Path 节点,而是使用 Canvas 节点来使用 GraphicsContext 执行渲染,因此无法修改先前渲染路径的颜色。 我正在尝试做类似
的事情Canvas canvas = …;
final GraphicsContext context = canvas.getGraphicsContext();
context.setFill(Color.BLUE);
context.fillRect(0,0,100,100);
…
…
稍后我想将路径中的像素重置为透明
context.setStroke(Color.TRANSPARENT);
context.beginPath();
context.moveTo(0, 0);
context.lineTo(10,10);
…
context.stroke();
但是,当应用混合时,这不起作用。在其他渲染库中,可以使用例如上面提到的一种混合/合成方法可以实现这一点,但直到现在我还无法在 javafx 中识别出这样的功能。
我还没有找到一种机制来根据路径清除 canvas。但您可以手动执行此操作:
- 创建一个蒙版,在上面绘制路径
- 手动混合图像和蒙版
应用了带有路径的遮罩的蓝色矩形示例:
public class ManualBlendWithMask extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Group root = new Group();
// create canvas
Canvas canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
// create canvas with blue rectangle
GraphicsContext canvasGC = canvas.getGraphicsContext2D();
canvasGC.setFill(Color.BLUE);
canvasGC.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
// create mask
Canvas mask = new Canvas( canvas.getWidth(), canvas.getHeight());
GraphicsContext maskGC = mask.getGraphicsContext2D();
maskGC.setFill(Color.WHITE);
maskGC.fillRect(0, 0, mask.getWidth(), mask.getHeight());
// draw path
maskGC.setStroke(Color.BLACK);
maskGC.setLineWidth(20);
maskGC.beginPath();
maskGC.moveTo(0, 0);
maskGC.lineTo(400, 400);
maskGC.stroke();
// get image from canvas
WritableImage srcImage = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
srcImage = canvas.snapshot(null, srcImage);
// get image from mask
WritableImage srcMask = new WritableImage((int) mask.getWidth(), (int) mask.getHeight());
srcMask = mask.snapshot(null, srcMask);
PixelReader maskReader = srcMask.getPixelReader();
PixelReader imageReader = srcImage.getPixelReader();
int width = (int) srcMask.getWidth();
int height = (int) srcMask.getHeight();
// create dest image
WritableImage dest = new WritableImage(width, height);
PixelWriter writer = dest.getPixelWriter();
// blend image and mask
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Color maskColor = maskReader.getColor(x, y);
Color imageColor = imageReader.getColor(x, y);
if (maskColor.equals(Color.WHITE)) {
writer.setColor(x, y, imageColor);
}
}
}
// clear canvas and fill it with the blended image
canvasGC = canvas.getGraphicsContext2D();
canvasGC.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
canvasGC.drawImage(dest, 0, 0);
// draw background with gradient
Rectangle rect = new Rectangle(400, 400);
rect.setFill(new LinearGradient(0, 0, 1, 1, true, CycleMethod.REFLECT, new Stop(0, Color.RED), new Stop(1, Color.YELLOW)));
// show nodes
root.getChildren().addAll(rect, canvas);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
当然,如果有人能想出一个使用 BlendModes 的例子就更好了。
在看到 2015 年的几篇帖子之后,我发现这仍然没有被添加到 javaFX
中,这让我很惊讶。据我所知,AWT
中有一个 "DST-OUT" 混合选项,那么为什么 javaFX
中没有?!
无论如何..我在找出自己的解决方案后回到这里,所以我可以分享我的解决方案,并希望能帮助下一个来这里寻找答案的不安分的灵魂。
我的具体需要是在基于 SVGPath
的区域中使 canvas 透明。所以这是我的解决方案:
private void clearPath(GraphicsContext gc, SVGPath path) {
int xstart = (int) path.getLayoutX();
int xend = (int) (xstart + path.getLayoutBounds().getMaxX());
int ystart = (int) path.getLayoutY();
int yend = (int) (ystart + path.getLayoutBounds().getMaxY());
PixelWriter pw = gc.getPixelWriter();
for (int x = xstart; x <= xend; x++) {
for (int y = ystart; y <= yend; y++) {
if(path.contains(new Point2D(x, y))) {
pw.setColor(x, y, Color.TRANSPARENT);
}
}
}
}