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

来源:互联网 发布:java tostring 编辑:程序博客网 时间:2024/05/17 02:21

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

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

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的基础。它在内存中保存数据,同时提供了存储,解析和获取像素数据的方法。因为BufferedImageImage的子类,它可以使用GraphicsGraphics2D中接受Image参数的方法渲染。

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

光栅执行以下功能:

l         表示图像的矩形坐标。

l         在内存中维护图像数据

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

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

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

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

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

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

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

 

1.1 读取加载图像

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

Java 2D支持将这些外部图像格式加载到BufferedImage格式,使用javax.imageio包中的I/O API进行。Image I/OGIF, PNG, JPEG, BMP, WBMP直接支持。Image I/O同时允许程序员和管理员添加其他格式的支持。例如,支持TIFFJPEG 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中运行,那么可以很容易的从appletcodebase中得到图像。

 

         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

GIFJPG有更低的色彩丢失率,支持半透明

不支持动画

JPG

对于照片图像处理很好

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

 

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

原创粉丝点击