在javafx中创建一个图像叠加蒙版

时间:2015-01-27 12:19:02

标签: image javafx imageview overlay mask

我试图做一件简单的事情。我有一个二进制图像,我想要的是在彩色图像上叠加二进制图像,但二进制图像中的白色像素应为红色,黑色透明。 我已经习惯了JavaFx,但我还是坚持这个。我知道我可以通过使用PixelReader迭代所有像素来实现它,但我确信有一种更简单的方法。我尝试使用某种混合效果但到目前为止没有运气。 我认为它应该类似于: How to Blend two Image in javaFX

Example of what I want to achieve

我想出了这个:         Image image = new Image(" /circle.jpg" ;, false);         ImageView iv = new ImageView(image);

Image mask = new Image("/mask.jpg", false);
ImageView ivMask = new ImageView(mask);

Rectangle r = new Rectangle(mask.getWidth(), mask.getHeight());
r.setFill(Color.RED);

r.setBlendMode(BlendMode.MULTIPLY); // sets the white area red

Group g = new Group(ivMask, r);   // sets the white area red


// this is not working as expected
iv.setBlendMode(BlendMode.DIFFERENCE);

Group g2 = new Group(iv, g);

感谢您的任何建议! 如果您认为,按像素处理比仅创建叠加更快,请告诉我。

像素阅读器的解决方案是:

Pane root = new Pane();

// read the underlaying image
root.getChildren().add(new ImageView(new Image("/src.jpg")));

Image mask = new Image("/mask.jpg");
PixelReader pixelReader = mask.getPixelReader();

Canvas resultCanvas = new Canvas();
root.getChildren().add(resultCanvas);

GraphicsContext resultLayer = resultCanvas.getGraphicsContext2D();
for (int y = 0; y < mask.getHeight(); y++) {
  for (int x = 0; x < mask.getWidth(); x++) {
    if( pixelReader.getColor(x, y).equals(Color.WHITE) ){
      resultLayer.fillRect(x, y, 1.0, 1.0);
    }
  }
}   

干杯!

1 个答案:

答案 0 :(得分:3)

你做错了什么

差异运算符不是基于是否设置像素的二进制差异而是RGB组件的差异,因此不是实心红色叠加,而是获得多色叠加,因为差异在于混合图像的RGB分量在像素之间不同。

<强>背景

您尝试使用混合模式执行与masked bit-blit operation类似的操作(基本上是OR,然后是基于黑色蒙版上的白色的像素数据的AND)。 JavaFX 8中的内置混合可能有点棘手。

您可以在混合API中为bit-blt样式基础创建一个功能请求,以及公开像porter duff compositing这样的完整Swing has实现,以便底层混合引擎具有更多功能功率可能更容易使用。

<强>替代

首选的方法是在图像编辑器(如photoshop)中预处理遮罩,将黑色部分转换为Alpha通道 - 然后您可以将遮罩层叠在原始图像上,默认合成模式将接受它。

要使支持alpha的蒙版成为红色,您可以使用mask.setBlendMode(BlendMode.RED)(或者您可以在图像编辑器中对蒙版进行预着色,然后再在程序中使用它。)

另一种选择是您在问题中使用的PixelReader解决方案(如果您无法预先将掩码转换为使用alpha,我认为这很好。)

混合操作可以在适当的硬件上进行硬件加速。因此,如果您经常使用混合物,可能会更快(但是您必须在大型图像上快速运行许多混合才能真正注意到任何类型的性能差异。)

使用混合操作的示例解决方案

示例输出

blended

输入图片

original.jpg

original

stencil.jpg

stencil

代码

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Blended extends Application {
    @Override
    public void start(Stage stage) {
        Image original = new Image(
            getClass().getResourceAsStream("original.jpg")
        );

        Image stencil = new Image(
            getClass().getResourceAsStream("stencil.jpg")
        );

        // first invert the stencil so that it is black on white rather than white on black.
        Rectangle whiteRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
        whiteRect.setFill(Color.WHITE);
        whiteRect.setBlendMode(BlendMode.DIFFERENCE);

        Group inverted = new Group(
                new ImageView(stencil),
                whiteRect
        );

        // overlay the black portions of the inverted mask onto the image.
        inverted.setBlendMode(BlendMode.MULTIPLY);
        Group overlaidBlack = new Group(
                new ImageView(original),
                inverted
        );

        // create a new mask with a red tint (red on black).
        Rectangle redRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
        redRect.setFill(Color.RED);
        redRect.setBlendMode(BlendMode.MULTIPLY);

        Group redStencil = new Group(
                new ImageView(stencil),
                redRect
        );

        // overlay the red mask on to the image.
        redStencil.setBlendMode(BlendMode.ADD);
        Group overlaidRed = new Group(
                overlaidBlack,
                redStencil
        );

        // display the original, composite image and stencil.
        HBox layout = new HBox(10);
        layout.getChildren().addAll(
                new ImageView(original),
                overlaidRed,
                new ImageView(stencil)
        );
        layout.setPadding(new Insets(10));
        stage.setScene(new Scene(layout));
        stage.show();
    }

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