Java开发笔记(一百二十五)AWT图像加工
前面介绍了如何使用画笔工具Graphics绘制各种图案,然而Graphics并不完美,它的遗憾之处包括但不限于:
1、不能设置背景颜色;
2、虽然提供了平移功能,却未提供旋转功能与缩放功能;
3、只能在控件上作画,无法将整幅画保存为图片;
有鉴于此,AWT提供了Graphics的升级版名叫Graphics2D,这个二维画笔不但继承了画笔的所有方法,而且拓展了好几个实用的方法,包括设置背景色的setBackground方法,旋转画布的rotate方法,缩放画布的scale方法等。尤为关键的是,Graphics2D允许在图像缓存BufferedImage上作画,意味着二维画笔的绘图成果能够保存为图片文件。这可是重大的功能改进,因为一旦保存为图片,以后就能随时拿出来用,不必每次都重新绘画了。
那么要怎样获得二维画笔呢?这还得从缓存图像BufferedImage说起。之前获取缓存图像的时候,是通过ImageIO工具把图片文件读到BufferedImage中,完全按照已有的图片构建缓存图像。其实直接调用BufferedImage的构造方法,也能创建一个空的缓存图像对象,接着调用该对象的createGraphics方法,即可创建并获取新图像的二维画笔,然后使用二维画笔就能在缓存图像上作画了。譬如要旋转某个缓存图像,则利用二维画笔Graphics2D实现的方法代码定义如下:
// 旋转图像。输入参数依次为:原图像、旋转角度 public static BufferedImage rotateImage(BufferedImage origin, int rotateDegree) { int width = origin.getWidth(); // 获取原图像的宽度 int height = origin.getHeight(); // 获取原图像的高度 int imageType = origin.getType(); // 获取原图像的颜色类型 // 创建与原图像同样尺寸的新图像 BufferedImage newImage = new BufferedImage(width, height, imageType); // 创建并获取新图像的画笔 Graphics2D graphics2d = newImage.createGraphics(); // 以原图像的中点为圆心,将画布按逆时针旋转若干角度 graphics2d.rotate(Math.toRadians(rotateDegree), width / 2, height / 2); // 使用新图像的画笔绘制原图像,也就是把原图像画到新图像上 graphics2d.drawImage(origin, 0, 0, null); return newImage; // 返回加工后的新图像 }
注意到上述代码调用BufferedImage的构造方法传入了三个参数,分别是新图像的宽度、高度和颜色类型。其中颜色类型常见的有两种:一种为BufferedImage.TYPE_4BYTE_ABGR,它表示四个字节的颜色模型,有三个字节分别表示蓝色、绿色和红色,还有一个字节表示透明度,这样总共有四个字节共32位,该类型等同于Windows平台上的32位真彩色;另一种颜色类型为BufferedImage.TYPE_3BYTE_BGR,它只有三个字节分别表示蓝色、绿色和红色,与TYPE_4BYTE_ABGR相比少了一个字节的透明度,这样加起来才24位,由于少了透明度信息,因此该类型接近于不透明的JPG图片格式。
接下来回到主界面的代码中,先在窗口上添加一个演示用的图像视图,并从本地图片构建一个原始的缓存图像,此时的控件初始化代码示例如下:
ImageView imageView= new ImageView(); // 创建一个图像视图 // 把输入流中的图片数据读到缓存图像 BufferedImage origin = ImageIO.read(TestChange.class.getResourceAsStream("apple.png")); // 设置图像视图的宽高 imageView.setSize(origin.getWidth(), origin.getHeight()); imageView.setImage(origin); // 设置图像视图的缓存图像 Panel panelCenter = new Panel(); // 创建中央面板 panelCenter.add(imageView); // 在中央面板上添加图像视图 frame.add(panelCenter, BorderLayout.CENTER); // 把中央面板添加到窗口的中间位置
然后在窗口上放置一个旋转按钮,单击该按钮时将命令图像往顺时针方向旋转90度,于是在按钮的单击事件中添加以下的旋转处理代码:
// 将图像视图的尺寸设置为原图像的宽高 imageView.setSize(origin.getWidth(), origin.getHeight()); // 获得顺时针旋转90度后的新图像 BufferedImage newImage = ImageUtil.rotateImage(origin, 90); imageView.setImage(newImage); // 设置图像视图的缓存图像
运行以上的主界面测试代码,在弹出的窗口界面中,单击旋转按钮前后的效果参见下列两图所示。
从两张效果图的对比可知,界面展示的图像成功旋转过来了。
实现图像的旋转功能之后,缩放图像、平移图像也可分别通过scale方法和translate方法来实现,相应的方法代码如下所示:
// 缩放图像。输入参数依次为:原图像、缩放的比率 public static BufferedImage resizeImage(BufferedImage origin, double ratio) { int width = origin.getWidth(); // 获取原图像的宽度 int height = origin.getHeight(); // 获取原图像的高度 int imageType = origin.getType(); // 获取原图像的颜色类型 // 创建尺寸大小为缩放宽高的新图像 BufferedImage newImage = new BufferedImage((int)(width*ratio), (int)(height*ratio), imageType); // 创建并获取新图像的画笔 Graphics2D graphics2d = newImage.createGraphics(); graphics2d.scale(ratio, ratio); // 把画布的宽高分别缩放到指定比例 // 使用新图像的画笔绘制原图像,也就是把原图像画到新图像上 graphics2d.drawImage(origin, 0, 0, null); return newImage; // 返回加工后的新图像 } // 平移图像。输入参数依次为:原图像、水平方向上的平移距离、垂直方向上的平移距离 public static BufferedImage translateImage(BufferedImage origin, int translateX, int translateY) { int width = origin.getWidth(); // 获取原图像的宽度 int height = origin.getHeight(); // 获取原图像的高度 int imageType = origin.getType(); // 获取原图像的颜色类型 // 创建与原图像同样尺寸的新图像 BufferedImage newImage = new BufferedImage(width, height, imageType); // 创建并获取新图像的画笔 Graphics2D graphics2d = newImage.createGraphics(); // 把画笔移动到指定的坐标点 graphics2d.translate(translateX, translateY); // 使用新图像的画笔绘制原图像,也就是把原图像画到新图像上 graphics2d.drawImage(origin, 0, 0, null); return newImage; // 返回加工后的新图像 }
缩放图像和平移图像的演示界面效果分别如下列两图所示。
除了旋转、缩放、平移这三种常见的图像变换操作,还有裁剪与翻转两种处理动作,其中参见用到了clipRect方法,而翻转用到了带十个参数的drawImage方法。下面是裁剪图像和翻转图像的方法定义:
// 裁剪图像。输入参数依次为:原图像、裁剪的比率 public static BufferedImage clipImage(BufferedImage origin, double ratio) { int width = origin.getWidth(); // 获取原图像的宽度 int height = origin.getHeight(); // 获取原图像的高度 int imageType = origin.getType(); // 获取原图像的颜色类型 // 创建尺寸大小为裁剪比例的新图像 BufferedImage newImage = new BufferedImage((int)(width*ratio), (int)(height*ratio), imageType); // 创建并获取新图像的画笔 Graphics2D graphics2d = newImage.createGraphics(); // 把画笔的绘图范围裁剪到从左上角到右下角的指定区域, // 其中左上角的坐标为(0,0),右下角的坐标为(width*ratio,height*ratio) graphics2d.clipRect(0, 0, (int)(width*ratio), (int)(height*ratio)); // 使用新图像的画笔绘制原图像,也就是把原图像画到新图像上 graphics2d.drawImage(origin, 0, 0, null); return newImage; // 返回加工后的新图像 } // 水平翻转图像。输入参数依次为:原图像 public static BufferedImage flipImage(BufferedImage origin) { int width = origin.getWidth(); // 获取原图像的宽度 int height = origin.getHeight(); // 获取原图像的高度 int imageType = origin.getType(); // 获取原图像的颜色类型 // 创建与原图像同样尺寸的新图像 BufferedImage newImage = new BufferedImage(width, height, imageType); // 创建并获取新图像的画笔 Graphics2D graphics2d = newImage.createGraphics(); // 使用新图像的画笔在目标位置绘制指定尺寸的原图像 // 其中目标区域的左上角坐标为(0,0),右下角坐标为(width,height) // 对于水平翻转的情况,原图像的起始坐标为(width,0),终止坐标为(0,height) graphics2d.drawImage(origin, 0, 0, width, height, width, 0, 0, height, null); // 对于垂直翻转的情况,原图像的起始坐标为(0,height),终止坐标为(width,0) //graphics2d.drawImage(origin, 0, 0, width, height, 0, height, width, 0, null); return newImage; // 返回加工后的新图像 }
裁剪图像和翻转图像的演示界面效果分别如下列两图所示。
更多Java技术文章参见《Java开发笔记(序)章节目录》