这个章节介绍Image Ops,这个API可以使你在你的javaFX应用中读写像素。你将会学到如果从图片中读取像素和写像素到图片中或者创建
一个快照。
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方法为新图片添加新数据。执行
代码显示如下,我们可以看到这个图片比之前的更明亮些:
之前的例子已经成功的获得和修改了像素的颜色,这个例子使用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); } }