这个章节介绍Image Ops,这个API可以使你在你的javaFX应用中读写像素。你将会学到如果从图片中读取像素和写像素到图片中或者创建
一个快照。
Image Ops API概述
Image Ops API 由下面的类和接口组成,这些类在javafx.scene.image包中
Image:代表了一个图片,这个类提供了一个PixelReader类,这个类可以直接从图片中读取像素
WritableImage:Image的子类,提供了PixelWriter类,这个类可以写像素到图片中。会创建一个空的WritableImage,直到你写
素到这个对象中。
PrixelReader:这是一个接口,定义了读取像素的方法。
PixelWriter:这是一个接口,定义了向WritableImage对象中写像素的方法。
PixelFormat:为格式化的像素数据定义了布局。
WritablePixelFormat:PixelFormat的子类,代表了可存储所有颜色的像素格式。它可以当做向任意图片写像素的目标格式。
下面我们用实例介绍这个API的使用
从图片读像素
你可能对javafx.scene.image.Image类已经熟悉了,它可以使你在javaFX应用中操作图片,下面的例子想我们展示了如何加载oracle.com
网址中的JavaF图标并把这个图标加入到javaFX场景图中。代码如下:
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;
public class ImageOpsTest extends Application {
@Override
public void start(Stage primaryStage) {
// Create Image and ImageView objects
Image image = new Image("http://docs.oracle.com/javafx/"
+ "javafx/images/javafx-documentation.png");
ImageView imageView = new ImageView();
imageView.setImage(image);
// Display image on screen
StackPane root = new StackPane();
root.getChildren().add(imageView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Image Read Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
运行项目,如下图所示:
下面我们修改代码,从像素中读取颜色。我们可以调用getPixelReader()方法,或者使用getColor(x,y)方法。会返回一个PixelReader对
象,我们可以利用这个对象来获取指定坐标的像素颜色。代码如下:
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.paint.Color;
public class ImageOpsTest extends Application {
@Override
public void start(Stage primaryStage) {
// Create Image and ImageView objects
Image image = new Image("http://docs.oracle.com/javafx/"
+ "javafx/images/javafx-documentation.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());
// Determine the color of each pixel in the image
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());
}
}
// Display image on screen
StackPane root = new StackPane();
root.getChildren().add(imageView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Image Read Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
这段代码直接用一个循环读取了图片上所有坐标位置的像素颜色。输出部分如下:
... // beginning of output omitted
Pixel color at coordinates (117,27) 0x95a7b4ff
R = 0.5843137502670288
G = 0.6549019813537598
B = 0.7058823704719543
Opacity = 1.0
Saturation = 0.17222220767979304
Pixel color at coordinates (118,27) 0x2d5169ff
R = 0.1764705926179886
G = 0.3176470696926117
B = 0.4117647111415863
Opacity = 1.0
Saturation = 0.5714285662587809
... // remainder of output omitted
你可能想改变像素的颜色并且把这些改变显示到屏幕上,想想一下,图片对象是只读的,你要是想写新数据,你需要一个WritebleImage
实例。
写像素到图片:
下面我们修改上面的例子。代码如下:
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("http://docs.oracle.com/javafx/"
+ "javafx/images/javafx-documentation.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());
// Now write a brighter color to the PixelWriter.
color = color.brighter();
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);
}
}
这个例子创建了一个WritableImage,这个对象和之前的Iamge对象的宽高是一样的。然后获得一个PixelWriter对象,这个对象负责写信的
像素数据到新的图片。通过brighter()方法获得一个更明亮的像素颜色,然后调用pixelWriter.setColor方法为新图片添加新数据。执行
代码显示如下,我们可以看到这个图片比之前的更明亮些:
使用字节数组和PixelFormats
之前的例子已经成功的获得和修改了像素的颜色,这个例子使用PixelFormat来指定像素数据写入的方法。使用Canvas代替了ImageView.
import java.nio.ByteBuffer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ImageOpsTest extends Application {
// Image Data
private static final int IMAGE_WIDTH = 10;
private static final int IMAGE_HEIGHT = 10;
private byte imageData[] =
new byte[IMAGE_WIDTH * IMAGE_HEIGHT * 3];
// Drawing Surface (Canvas)
private GraphicsContext gc;
private Canvas canvas;
private Group root;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("PixelWriter Test");
root = new Group();
canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
gc = canvas.getGraphicsContext2D();
createImageData();
drawImageData();
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
private void createImageData() {
int i = 0;
for (int y = 0; y < IMAGE_HEIGHT; y++) {
int r = y * 255 / IMAGE_HEIGHT;
for (int x = 0; x < IMAGE_WIDTH; x++) {
int g = x * 255 / IMAGE_WIDTH;
imageData[i] = (byte) r;
imageData[i + 1] = (byte) g;
i += 3;
}
}
}
private void drawImageData() {
boolean on = true;
PixelWriter pixelWriter = gc.getPixelWriter();
PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance();
for (int y = 50; y < 150; y += IMAGE_HEIGHT) {
for (int x = 50; x < 150; x += IMAGE_WIDTH) {
if (on) {
pixelWriter.setPixels(x, y, IMAGE_WIDTH,
IMAGE_HEIGHT, pixelFormat, imageData,
0, IMAGE_WIDTH * 3);
}
on = !on;
}
on = !on;
}
// Add drop shadow effect
gc.applyEffect(new DropShadow(20, 20, 20, Color.GRAY));
root.getChildren().add(canvas);
}
}
运行如下图所示:
创建一个快照
javafx.scene.Scene类提供了一个快照的方法,该方法返回WritableImage。当使用java的IamageIO类的时候,我们可以在文件系统中存储
一个快照。代码如下:
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class ImageOpsTest extends Application {
// Image Data
private static final int IMAGE_WIDTH = 10;
private static final int IMAGE_HEIGHT = 10;
private byte imageData[] = new byte[IMAGE_WIDTH * IMAGE_HEIGHT * 3];
// Drawing Surface (Canvas)
private GraphicsContext gc;
private Canvas canvas;
private Group root;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("PixelWriter Test");
root = new Group();
canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
gc = canvas.getGraphicsContext2D();
createImageData();
drawImageData();
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
//Take snapshot of the scene
WritableImage writableImage = scene.snapshot(null);
// Write snapshot to file system as a .png image
File outFile = new File("imageops-snapshot.png");
try {
ImageIO.write(SwingFXUtils.fromFXImage(writableImage, null),
"png", outFile);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
private void createImageData() {
int i = 0;
for (int y = 0; y < IMAGE_HEIGHT; y++) {
System.out.println("y: " + y);
int r = y * 255 / IMAGE_HEIGHT;
for (int x = 0; x < IMAGE_WIDTH; x++) {
System.out.println("\tx: " + x);
int g = x * 255 / IMAGE_WIDTH;
imageData[i] = (byte) r;
imageData[i + 1] = (byte) g;
System.out.println("\t\tR: " + (byte) r);
System.out.println("\t\tG: " + (byte) g);
i += 3;
}
}
System.out.println("imageData.lengthdrawImageData: " + imageData.length);
}
private void drawImageData() {
boolean on = true;
PixelWriter pixelWriter = gc.getPixelWriter();
PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance();
for (int y = 50; y < 150; y += IMAGE_HEIGHT) {
for (int x = 50; x < 150; x += IMAGE_WIDTH) {
if (on) {
pixelWriter.setPixels(x, y, IMAGE_WIDTH,
IMAGE_HEIGHT, pixelFormat,
imageData, 0, IMAGE_WIDTH * 3);
}
on = !on;
}
on = !on;
}
// Add drop shadow effect
gc.applyEffect(new DropShadow(20, 20, 20, Color.GRAY));
root.getChildren().add(canvas);
}
}