NaturalQuestioner
NaturalQuestioner

Reputation: 95

How to change background of an image in javafx

I have a javafx application and I have a rectangle in my ancorpane and I use rectangle.setFill() to fill the rectangle with an image. Now assume the image is a round red ball but we all know that the png file is a rectangle. ( with a width and height ) so there are some parts of this png file that are white. ( outside the red circle ). I want to know if it's possible to change this white part of the image with another color, let's say blue.

So I don't want to set a background color to my anchorpane, if I did that, after rectangle.setFill() line gets executed, it fills the rectangle with the image I put in the command without considering the background color of anchorpane. I want to change the color of the background of the "png file" I put in the rectangle.setFill() inside the code.

If you need to know a specific definition of background, lets say that I want to change the color of every white colored pixel in my png file to blue color. (Not manually before running program but inside the code)

Upvotes: 0

Views: 600

Answers (2)

Lucas Favaro Borsatto
Lucas Favaro Borsatto

Reputation: 114

Here is another way:

package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.paint.Color;
import javafx.scene.image.WritableImage;
 
public class ImageOpsTest extends Application {
    
    @Override
    public void start(Stage primaryStage) {
     
        // Create Image and ImageView objects
        Image image = new Image("file:///home/paradigma/Imagens/photo.png");
        
        
        ImageView imageView = new ImageView();
        imageView.setImage(image);
       
        // Obtain PixelReader
        PixelReader pixelReader = image.getPixelReader();
        System.out.println("Image Width: "+image.getWidth());
        System.out.println("Image Height: "+image.getHeight());
        System.out.println("Pixel Format: "+pixelReader.getPixelFormat());
        
        // Create WritableImage
         WritableImage wImage = new WritableImage(
                 (int)image.getWidth(),
                 (int)image.getHeight());
         PixelWriter pixelWriter = wImage.getPixelWriter();
       
        // Determine the color of each pixel in a specified row
        for(int readY=0;readY<image.getHeight();readY++){
            for(int readX=0; readX<image.getWidth();readX++){
                Color color = pixelReader.getColor(readX,readY);
                System.out.println("\nPixel color at coordinates ("+
                        readX+","+readY+") "
                        +color.toString());
                System.out.println("R = "+color.getRed());
                System.out.println("G = "+color.getGreen());
                System.out.println("B = "+color.getBlue());
                System.out.println("Opacity = "+color.getOpacity());
                System.out.println("Saturation = "+color.getSaturation());
               
                if (color.equals(Color.WHITE)) color = color.BLUE;
                pixelWriter.setColor(readX,readY,color);
            }
        }
        
        // Display image on screen
        imageView.setImage(wImage);
        StackPane root = new StackPane();
        root.getChildren().add(imageView);
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("Image Write Test");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}

The "System.out.println" codes is present to give a idea of the components colors of each pixel, nevertheless, make the code slow. If this information is irrelevant the print's can be commented (this will make the code faster).

Upvotes: 0

James_D
James_D

Reputation: 209330

You can get a PixelReader from the original image which you can use to query the color of the individual pixels.

You can then create a PixelBuffer and copy the pixels over to it, adjusting any that need to be adjusted.

Then create a WritableImage from the PixelBuffer. You can even adjust the pixel data dynamically, calling updateBuffer on the buffer to make sure the image updates.

Here's an example, where you can choose a color to replace the white background with:

import java.nio.IntBuffer;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ColorPicker;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelBuffer;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelReader;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;


public class App extends Application {

    @Override
    public void start(Stage stage) {
        Image originalImage = createImage();
        ImageView orig =  new ImageView(originalImage);
        
        ColorPicker colorPicker = new ColorPicker(Color.BLUE);
        
        int width = (int) originalImage.getWidth();
        int height = (int) originalImage.getHeight();
        
        int[] pixels = new int[width * height];
        PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<IntBuffer>(width, height, IntBuffer.wrap(pixels), PixelFormat.getIntArgbPreInstance());
        adjustImage(originalImage, colorPicker.getValue(), pixelBuffer, pixels);
        
        ImageView adjusted = new ImageView(new WritableImage(pixelBuffer));
        
        HBox images = new HBox(5, orig, adjusted);
        
        colorPicker.valueProperty().addListener((obs, oldColor, newColor) -> adjustImage(originalImage, newColor, pixelBuffer, pixels));
        
        HBox controls = new HBox(5, colorPicker);
        
        BorderPane root = new BorderPane(images);
        root.setTop(controls);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
        
    }
    
    private void adjustImage(Image original, Color newColor, PixelBuffer<IntBuffer> buffer, int[] pixels) {
        
        int alpha = (int)(newColor.getOpacity() * 255) ;
        int r = (int)(newColor.getRed() * 255);
        int g = (int)(newColor.getGreen() * 255);
        int b = (int)(newColor.getBlue() * 255);
        
        int replacement = alpha << 24 | r << 16 | g << 8 | b ;
        
        int width = (int)original.getWidth() ;
        int height = (int)original.getHeight();
        PixelReader pixelReader = original.getPixelReader() ;
        for (int y = 0 ; y < height ; y++) {
            for (int x = 0 ; x < width ; x++) {
                int argb = pixelReader.getArgb(x, y);
                if (argb == 0xFFFFFFFF) {
                    pixels[x+y*width] = replacement ;
                } else {
                    pixels[x+y*width] = argb ;
                }
            }
        }
        buffer.updateBuffer(buff -> null);
    }
    
    private Image createImage() {
        
        // In real life here you can just read an image from a resource in the normal way
        // This just creates an image on the fly to make the example stand alone
        
        int width = 400 ;
        int height = 400 ;
        int[] pixels = new int[width*height];
        for (int y = 0 ; y < height ; y++) {
            for (int x = 0 ; x < width ; x++) {
                if ((x-200)*(x-200) + (y-200)*(y-200) < 40000) {
                    pixels[x+y*width] = 0xffff0000 ;
                } else {
                    pixels[x+y*width] = 0xffffffff ;
                }
            }
        }
        return new WritableImage(new PixelBuffer<IntBuffer>(width, height, IntBuffer.wrap(pixels), PixelFormat.getIntArgbPreInstance()));
    }

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

}

Upvotes: 2

Related Questions