I\'m trying to do a simple thing. I have a binary image and all I want is to overlay the binary image on a color image, but the white pixels in the binary image should be re
What you are Doing Wrong
The difference operator isn't a binary difference based on whether a pixel is set instead it is a difference in the RGB components, so instead of a solid red overlay, you will get a multi-colored overlay because the difference in the RGB components of the blended images differs between pixels.
Background
You are trying to do something similar to a masked bit-blit operation with blend modes (basically, an OR then an AND of pixel data based on a white on black mask). It is possible though a little tricky with the built-in blends in JavaFX 8.
You could create a feature request for additional support in the blend API for bit-blt style basics as well as exposing a full porter duff compositing implementation like Swing has so that the underlying blend engine has a bit more power and is possibly a little easier to use.
Alternatives
The preferred thing to do would be to pre-process your mask in an image editor like photoshop to convert the black part to an alpha channel - then you can just layer your mask on top of your original and the default compositing mode will take of it.
To make your alpha enabled mask red, you could just use mask.setBlendMode(BlendMode.RED)
(or you could pre-color the mask in an image editor before using it in your program).
Another alternative is the PixelReader solution you have in your question (which I think is fine if you are unable to pre-convert your mask to use alpha).
The blend operations can be hardware accelerated on appropriate hardware. So potentially using a blend could be faster if you are doing it very often (but you would have to have many blends being run very quickly on large images to really notice any kind of performance difference).
Sample Solution Using Blend Operations
Sample Output
Input Images
original.jpg
stencil.jpg
Code
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();
}
}