2016-12-05 85 views
1

编辑:我已更改了一些代码以反映AlamasB所说的内容。我仍然不明白应该如何填充字节数组以及需要做什么转换。将图像的像素复制到java中的画布中

我试图操纵用户上传的图像的像素,并将新图像写入画布。现在我只是试图复制原始图像而不改变像素的RGB。我一直在同一个地方停留了很长一段时间,无法确定下一步该去哪里。在代码的第一部分,我制作了用户上传的原始图像的副本。

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; 
} 

编辑:不再使用此功能。下一个功能让我很困惑。我指的是http://docs.oracle.com/javafx/2/image_ops/jfxpub-image_ops.htm作为参考,但我无法弄清楚发生了什么。

//... 
//... 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; 
    } 
} 
+0

所以是oracle的方法例如不工作的问题,或者您希望它是如何工作的解释? – fakataha

+1

[this](http://docs.oracle.com/javafx/2/api/javafx/scene/image/PixelWriter.html#setPixels(int,%20int,%20int,%20int,%20javafx.scene.image .PixelFormat,%20int [],%20int,%20int))是开始阅读的好时机。您提供的方法不会创建图像的副本。 – Paul

+0

我很高兴你链接到setPixels()方法,因为这就是让我困惑的东西。它表示前两个参数是写入数据的x和y坐标。它是指画布的x和y,还是我需要知道相对于我的根对象的x和y位置? –

回答

0

考虑到PixelReader提供getARGB在这种情况下你最好的选择可能是使用32位表示。随后将其转换为4字节数组,即width * height * 4。最后使用setARGB来绘制图像。

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); 

或者,你可以用颜色的getOpacity()RGBA得到A。再次使用32位表示。

至少这将为您提供一个最小的工作示例,然后您可以扩展。

这里有一个快速的版本(自包含的,只是复制和粘贴):

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); 
    } 
} 
+0

这帮助了我,但我仍然困惑。你的例子中是什么argb2?文档说getArgb()和setArgb()都使用32位格式,因此需要完成哪种类型的转换。我也不知道如何填充imageData字节数组。我更新了我的OP,如果你想看一看 –

+0

在你的例子中你用图像数据填充'byte []',我只是指出你应该用4个字节来表示一个像素,而不是3个。如果'argb'是你的原始未修改像素数据,那么'argb2'是修改过的像素数据。通过转换,我的意思是将32位值转换为4字节数组,因为您在示例中将其用作中间格式。 – AlmasB

+0

请参阅答案,并附上一个工作示例 – AlmasB