Java标准教程:Java 2D绘图--第5章 使用图像

在概述一节中,图像使用宽度和高度描述,以像素为单位,有自己独立于绘图平面的坐标系。

使用图像时有很多普通的任务。

l         将图像外部的GIF, PNG JPEG 图像文件中加载到Java 2D 内部表示中。

l         直接创建Java 2D 图像听且绘制它。

l         在绘制平面中绘制Java 2D 图像的内容。

l         Java 2D 图像保存在外部的GIF, PNG,  JPEG 图像文件中。

本课教您基本的加载,展示和保存图像的方法。

使用图像有两个类必须知道:

l         java.awt.Image 是超类,它以矩形像素数组的方式表示图像。

l         java.awt.image.BufferedImage 扩展了Image 类,允许应用程序直接操作图像数据(例如,获取或设置像素的颜色)。应用程序可以直接创建这个类的实例。

BufferedImage 类是Java 2D 即时模式图像API 的基础。它在内存中保存数据,同时提供了存储,解析和获取像素数据的方法。因为BufferedImage 是Image 的子类,它可以使用Graphics 和Graphics2D 中接受Image 参数的方法渲染。

BufferedImage 的本质是一个带有可访问数据缓存的Image 。直接操作BufferedImage 会更高效。BufferedImage 有图像数据的ColorModel 和光栅。ColorModel 提供了图像像素数据的颜色解释。

光栅执行以下功能:

l         表示图像的矩形坐标。

l         在内存中维护图像数据

l         提供从单一图像数据缓冲区中创建多个子图像的机制。

l         提供了访问图像内特定像素的方法。

图像的基本操作在以下几节中介绍:

l         读取/ 加载图像
本节描述如何从外部图像文件中,使用图像I/O API 将图像加载到Java 应用程序中。

l         绘制图像
本节介绍如何使用Graphics 和Graphics2D 类中的drawImage 方法显示图像。

l         创建并向图像中进行绘制
本节介绍如何创建图像,并且如何将图像作为绘制平面。

l         保存图像
本节介绍如何将图像保存成合适的格式。

 

1.1 读取加载图像

当您想象数字图像时,想一下数码相机中使用的JPEG 格式,和在web 页面中常用的GIF 格式。所有使用这些图像的程序必须首先将外部格式转换成内部格式。

Java 2D 支持将这些外部图像格式加载到BufferedImage 格式,使用javax.imageio 包中的I/O API 进行。Image I/O 对GIF, PNG, JPEG, BMP,  WBMP 直接支持。Image I/O 同时允许程序员和管理员添加其他格式的支持。例如,支持TIFF 和JPEG 2000 的插件就是单独提供的。

要加载制定图像,需要如下代码:

 

BufferedImage img = null;

try {

    img = ImageIO.read(new File("strawberry.jpg"));

} catch (IOException e) {

}

 

Image I/O 将文件内容当做JPEG 格式解析,同时将他们解码到Java 2D 可以直接使用的BufferedImage 中。

LoadImageApp.java 展示了如何展示图像。

如果代码在applet 中运行,那么可以很容易的从applet 的codebase 中得到图像。

 

         try {

             URL url = new URL(getCodeBase(), "strawberry.jpg");

             img = ImageIO.read(url);

         } catch (IOException e) {

         }

 

例子中的getCodeBase 方法返回包含applet 的目录的URL 。

下面的例子展示了如何使用getCodeBase 方法加载strawberry.jpg 文件。

LoadImageApp.java 包含这个例子的完整代码,同时appet 需要strawberry.jpg 图像文件。除了从文件或URL中读取之外,Image I/O 还可以从其他源中读取,例如InputStream 。

ImageIO.read() 是大多数应用程序最常用的API ,但javax.imageio.ImageIO 包含了其他静态方法,提供更高级的Image I/O API 。这个类中的方法表示了从图像中获取信息同时控制图像解码的API 的一小部分。

在保存图像一节将会介绍一些其他的Image I/O 。更详细的信息请参考Image I/O 的指导。

 

1.2 绘制图像

您已经知道,Graphics.drawImage 方法在指定位置绘制图像:

 

boolean Graphics.drawImage(Image img,

                           int x, int y,

                            ImageObserver observer);

 

X, y 位置表示图像的左上角。Observer 参数放应用程序异步更新图像。Observer 参数不经常直接使用,并且对于BufferedImage 类来说是不需要的,所以通常是null 。

上面的方法只有在绘制整个图像时才有用,它按照1:1 的比例将图像的像素映射到用户空间坐标中。有时,应用程序需要绘制一部分图像(或子图像),或者是拉伸了图像,以覆盖绘图平面的一部分区域,或者在绘制前进行转换或过滤。

drawImage() 方法的重载形式执行这些操作。例如,以下drawImage() 方法可以在特定的区域绘制特定的图像,同时进行拉伸以符合目标绘制平面的要求:

 

boolean Graphics.drawImage(Image img,

                           int dstx1, int dsty1, int dstx2, int dsty2,

                           int srcx1, int srcy1, int srcx2, int srcy2,

                            ImageObserver observer);

 

Src 参数表示需要拷贝和绘制的区域。Dest 参数展示了需要由源数据覆盖的区域。dstx1, dsty1 坐标定义了绘制的位置。通过以下公式计算目标区域的宽和高:(dstx2-dstx1), (dsty2-dsty1) 。如果源区域和目标区域的大小不同,Java 2D API 将自动执行图像拉伸或收缩。

以下代码将图片剪切成4 个部分,同时随机的将每个部分绘制到目标区域。

这个applet 的完整代码在JumbledImageApplet.java 中。这个例子使用以下代码绘制duke_skateboard.jpg图片。它遍历四个子图像,每次随机选择一个进行绘制。

 

        /* divide the image 'bi' into four rectangular areas and draw each

         * of these areas in to a different part of the image, so as to

         * jumble up the image.

         * 'cells' is an array which has been populated with values

         * which redirect drawing of one subarea to another subarea.

         */

        int cellWidth = bi.getWidth(null)/2;

        int cellHeight = bi.getHeight(null)/2;

        for (int x=0; x<2; x++) {

            int sx = x*cellWidth;

            for (int y=0; y<2; y++) {

                int sy = y*cellHeight;

                int cell = cells[x*2+y];

                int dx = (cell / 2) * cellWidth;

                int dy = (cell % 2) * cellHeight;

                 g.drawImage(bi,

                            dx, dy, dx+cellWidth, dy+cellHeight,

                            sx, sy, sx+cellWidth, sy+cellHeight,

                            null);

            }

        }

 

1.2.1 过滤图像

除了拷贝和拉伸图像外,Java 2D API 也可以过滤图像。过滤是在绘制或产生新图像的过程中,对原有图像的像素应用的一种算法。图像过滤器可以使用下面的方法进行使用:

 

void Graphics2D.drawImage(BufferedImage img,

                          BufferedImageOp op,

                          int x, int y)

 

BufferedImageOp 参数实现了过滤器。以下applet 表示一个在左上角绘制的文本。拖动滑块可以透过图像显示文本,也可以让图像变得透明。

以下代码展示了过滤操作通过在BufferedImage 上使用alpha channel 进行操作,同时使用RescaleOp 对象重新拉伸这个alpha channel 的过程。它同时指定了图像覆盖的度。

 

   /* Create an ARGB BufferedImage */

   BufferedImage img = ImageIO.read(imageSrc);

   int w = img.getWidth(null);

   int h = img.getHeight(null);

   BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

   Graphics g = bi.getGraphics();

   g.drawImage(img, 0, 0, null);

 

   /* Create a rescale filter op that makes the image 50% opaque */

   float[] scales = { 1f, 1f, 1f, 0.5f };

   float[] offsets = new float[4];

   RescaleOp rop = new RescaleOp(scales, offsets, null);

 

   /* Draw the image, applying the filter */

   g2d.drawImage(bi, rop, 0, 0);

 

完成的例子在SeeThroughImageApplet.java 中,包含了使用滑块从开始的50 %调整透明度的代码。这个例子需要duke_skateboard.jpg 图像。

RescaleOp 对象只是多种过滤器之一。Java 2D API 有以下多种内建的过滤器:

l         ConvolveOp :每个输出像素都围绕源图像中像素计算。可以用来让图像变得模糊或尖锐。

l         AffineTransformOp :这个过滤器通过在像素位置上进行转换,将源像素映射到目标的不同位置。

l         LookupOp :这个过滤器使用用用程序提供的查询表重新映射像素颜色。

l         RescaleOp :这个过滤器在颜色上乘以一定参数。可以用来让图像加亮或变暗,增加或减少透明度等。

下面的例子展示了上述的每种过滤器:

 

这个applet 的完整源代码在ImageDrawingApplet.java ,这个applet 需要bld.jpg 图片。

使用下拉列表可以选择可以选择对图像进行拉伸或过滤操作。

 

1.3 创建和绘制图像

我们已经知道如何加载一个已经存在的图像,它在您的系统或网络的任何位置创建和保存。但是,你可能想要创建一个新的图像作为像素数据的缓冲区。

这种情况下,您可以手动创建BufferedImage 对象,使用以下三个构造函数:

l         new BufferedImage(width, height, type) :使用预定义的图像类型之一创建BufferedImage 。

l         new BufferedImage(width, height, type, colorModel) :使用预定义的图像类型创建BufferedImage,可以是TYPE_BYTE_BINARY  TYPE_BYTE_INDEXED 。

l         new BufferedImage(colorModel, raster, premultiplied, properties) :使用指定的ColorModelRaster 创建新的BufferedImage 。

同时,我们也可以使用Component 类的方法。这些方法可以分析给定Component 的显示分辨率,或GraphicsConfiguration ,同时创建合适类型的图像。

l         Component.createImage(width, height)

l         GraphicsConfiguration.createCompatibleImage(width, height)

l         GraphicsConfiguration.createCompatibleImage(width, height, transparency)

GraphicsConfiguration 返回BufferedImage 类型的对象,但Component 返回Image 类型的对象,如果您需要一个BufferedImage 对象,这样可以执行在代码中instanceof 然后转换成BufferedImage 。

在以前的章节中已经提到,我们不止是在屏幕中渲染图像。图像自己可以被当做绘制平面。您可以使用BufferedImage 类的createGraphics() 干下面的事:

 

...

 

BufferedImage off_Image =

    new BufferedImage(100, 50, BufferedImage.TYPE_INT_ARGB);

 

Graphics2D g2 = off_Image.createGraphics();

 

另一个屏幕外的图像时自动的双缓存。这种特性可以在后台缓冲器中绘制图像,然后拷贝到屏幕中,而不是直接绘制在屏幕中,以避免动画闪烁。

Java 2D 也允许离线图像的硬件加速,这可以为从这些图像进行渲染和拷贝提供更好的性能。您可以使用Image 类的下列方法完成这项功能:

l         getCapabilities 方法决定图像是否已经被加速。

l         setAccelerationPriority 方法让您指定为图像使用的加速方式。

l         getAccelerationPriority 方法得到图像的加速重要性。

 

1.4  / 写图像

本节以javax.imageio 包的说明开始,它可以从外部图像格式的文件中家在图像,并转换成Java 2D 内部的BufferedImage 格式。然后说明如何使用Graphics.drawImage() 方法绘制图像,同时增加可选的过滤器。

最后的部分是将BufferedImage 对象保存为外部图像格式。这可以是最初由Image I/O 类从外部图像文件中加载的,同时可以使用Java 2D API 修改,或者可以是由Java 2D 创建的。

Image I/O 类提供了保存多种文件格式的简单方法,如下所示:

 

static boolean ImageIO.write(RenderedImage im,

                             String formatName,

                             File output) throws IOException

 

 

注意:BufferedImage 类实现了RenderedImage 接口。

 

formatName 参数选择要保存BufferedImage 的格式,如下:

 

try {

    BufferedImage bi = getMyImage(); // retrieve image

    File outputfile = new File("saved.png");

    ImageIO.write(bi, "png", outputfile);

} catch (IOException e) {

    ...

}

 

ImageIO.write 方法调用了实现写PNG 文件的代码。名词“插件”用来表示Image I/O 是可扩展的,同时可以支持多种格式。

通常可以使用以下标准图像格式:JPEG, PNG, GIF, BMP  WBMP 。

每种图像格式都有自己的优缺点:

 

 

优点

缺点

GIF

支持动画和透明像素

只支持256 色,不支持半透明效果

PNG

GIF 和JPG 有更低的色彩丢失率,支持半透明

不支持动画

JPG

对于照片图像处理很好

会因为压缩丢失精度,不应该作为文本,快照以及其他需要精确保存原来图形的应用程序

 

对于大多数应用程序来说,使用这些标准插件之一已经足够了。他们的优点是立即可用。Image I/O 类提供了支持其他可用的格式的插件式方法。如果你对一些文件格式感兴趣,并且需要在系统中读取或保存他们,您必须使用ImageIO 类的getReaderFormatNames 和getWriterFormatNames 方法。这些方法返回当前JRE 中支持的所有格式列表。

 

String writerNames[] = ImageIO.getWriterFormatNames();

 

返回值将包含所有已经安装的插件,同时他们都可以作为选择图像writer 的格式名。下面的代码展示了整个图像编辑/ 修改程序的建议版本,它使用ImageDrawingApplet.java 示例程序的修订版,如下:

l         首先使用Image I/O 加载图像。

l         用户从下拉列表中选择过滤器,然后绘制更新后的图像。

l         用户从下拉列表中选择保存格式。

l         然后文件选择器显示出来,同时用户选择在哪保存图像。

l         修改后的图像现在可以被其他桌面应用程序查看。

完整的代码在SaveImage.java 中。

本课您已经学习了Image I/O 的基本信息,它为写图像提供了扩展的支持,以及支持使用ImageWriter 插件最终控制编码过程。ImageIO 可以写多种图像,图像元数据,决定质量和大小的关系。更详细的信息请参考Java Image I/O API Guide.

你可能感兴趣的:(java,image,api,null,BI,applet)