Omega Collision
Omega Collision

Reputation: 125

Copying the pixels of an image to a canvas in java

EDIT: I've changed some of my code to reflect what AlamasB said. I still don't understand how I should be populating my byte array and what conversions I need to do.

I'm trying to manipulate the pixels of an image uploaded by the user and write the new image onto a canvas. Right now I'm just trying to copy the original image without changing the RGB of the pixels. I've been stuck in the same spot for quite a while now and can't figure out where to go next. In the first section of the code I make a copy of the original image uploaded by the user.

   Image image = imageView.getImage();
   PixelReader pixelReader = image.getPixelReader();
   PixelFormat format = pixelReader.getPixelFormat();

   int width= (int)image.getWidth();
   int height = (int) image.getHeight();

   GraphicsContext gc = canvas.getGraphicsContext2D();
   PixelWriter pw = gc.getPixelWriter();

   byte[] imageData = new byte[width * height * 4];
   imageData = createImageData(imageData, pixelReader, width, height); 

    //..        
    //...the next function populates my byte array with what's in the image 

  public byte[] createImageData(byte[] imageData, PixelReader pr, int width, int height){
    int i = 0;
    for(int y=0; y<height; y++){
        for(int x = 0; x < width; x++){
            int argb = pixelReader.getArgb(x, y);
            imageData[i] = (byte) argb;
            imageData[i+1] = (byte) argb;
            imageData[i+2] = (byte) argb;
            i+=3;
            pw.setArgb(x, y, argb);
        }
    }


    return imageData;
}

EDIT: No longer using this function. The next function is really confusing me. I'm referring to this http://docs.oracle.com/javafx/2/image_ops/jfxpub-image_ops.htm as a reference but I can't figure out what's going on.

//...  
//... the next function sets the pixels of the canvas to what's in the byte array

public void drawImageData(byte[] imageData, PixelWriter pw, int width, int height){
    boolean on = true;
    PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance();
    for(int y = 50; y < 150; y+=height){
        for(int x = 50; x < 150; x+=width){
            if(on){
                pw.setPixels(x, y, width, height, pixelFormat, imageData, 0, width*3);
            }
            on = !on;
        }
        on=!on;
    }
}

Upvotes: 1

Views: 2008

Answers (1)

AlmasB
AlmasB

Reputation: 3407

Considering that PixelReader provides getARGB your best bet in this case is probably to use 32-bit representation. Subsequently converting it to a 4-byte array, i.e. width * height * 4. Finally use setARGB to draw the image.

int argb = pixelReader.getArgb(x, y);
// populate imageData[i,i+1,i+2,i+3] by converting argb
...
// convert imageData[i,i+1,i+2,i+3] into a 32bit argb, say int argb2
pixelWriter.setArgb(x, y, argb2);

Alternatively, you can use Color's getOpacity() to get the A from RGBA. Again, using the 32-bit representation.

At the very least this will provide you with a minimal working example, which you can then extend.

Here's a quick version (self-contained, just copy and paste):

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.nio.ByteBuffer;
import java.util.Arrays;

public class ImageManipulationApp extends Application {

    private static final int APP_W = 800;
    private static final int APP_H = 600;

    private Parent createContent() {
        Image image = makeMockImage();
        byte[] imageData = imageToData(image);

        byte[] modifiedImageData = modify(imageData);
        Image modifiedImage = dataToImage(modifiedImageData);

        HBox root = new HBox(25);
        root.getChildren().addAll(new ImageView(image), new ImageView(modifiedImage));

        return root;
    }

    private Image makeMockImage() {
        WritableImage image = new WritableImage(APP_W / 2, APP_H);
        PixelWriter writer = image.getPixelWriter();

        for (int y = 0; y < APP_H; y++) {
            for (int x = 0; x < APP_W / 2; x++) {
                writer.setColor(x, y, Math.random() < 0.00005 ? Color.YELLOW : Color.BLACK);
            }
        }

        return image;
    }

    /**
     * Modifies the pixel data.
     *
     * @param data original image data
     * @return modified image data
     */
    private byte[] modify(byte[] data) {
        // this is where changes happen
        return data;
    }

    private byte[] imageToData(Image image) {
        int width = (int) image.getWidth();
        int height = (int) image.getHeight();

        byte[] data = new byte[width * height * 4];

        int i = 0;

        for (int y = 0; y < height; y++){
            for (int x = 0; x < width; x++){
                int argb = image.getPixelReader().getArgb(x, y);

                byte[] pixelData = ByteBuffer.allocate(4).putInt(argb).array();

                data[i++] = pixelData[0];
                data[i++] = pixelData[1];
                data[i++] = pixelData[2];
                data[i++] = pixelData[3];
            }
        }

        return data;
    }

    private Image dataToImage(byte[] data) {
        // if we don't know the image size beforehand we can encode width and height
        // into image data too
        WritableImage image = new WritableImage(APP_W / 2, APP_H);
        PixelWriter writer = image.getPixelWriter();

        int i = 0;
        for (int y = 0; y < APP_H; y++) {
            for (int x = 0; x < APP_W / 2; x++) {
                int argb = ByteBuffer.wrap(Arrays.copyOfRange(data, i, i + 4)).getInt();

                writer.setArgb(x, y, argb);

                i += 4;
            }
        }

        return image;
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.show();
    }

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

Upvotes: 1

Related Questions