java.awt
类 Graphics2D
java.lang.Object
继承者 java.awt.Graphics
继承者 java.awt.Graphics2D
public abstract class Graphics2Dextends Graphics
此 Graphics2D
类扩展 Graphics 类,以提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制。它是用于在 Java(tm) 平台上呈现二维形状、文本和图像的基础类。
坐标空间
所有传递到 Graphics2D
对象的坐标都在一个与设备无关并且名为用户空间的坐标系中指定,用户空间由应用程序使用。Graphics2D
对象包含一个 AffineTransform 对象作为其呈现状态的一部分,后者定义了如何将坐标从用户空间转换到设备空间中与设备有关的坐标。
设备空间中的坐标通常是指单个设备像素,并根据这些像素之间无限小的间距对齐。某些 Graphics2D
对象可用于捕获对存储器的呈现操作并存入图形元文件,以后可在未知物理分辨率的具体设备上重放。由于在捕获呈现操作时分辨率可能未知,所以 Graphics2D
Transform
的设置可将用户坐标转换为虚拟设备空间,该设备空间与目标设备的预期分辨率接近。如果估计值不正确,则在重放时可能需要进一步转换。
某些由呈现属性对象执行的操作发生在设备空间中,但所有 Graphics2D
方法都采用用户空间坐标。
每个 Graphics2D
对象都与一个定义呈现位置的目标关联。GraphicsConfiguration 对象定义呈现目标的特征,如像素格式和分辨率。在 Graphics2D
对象的整个生命周期中都使用相同的呈现目标。
创建 Graphics2D
对象时,GraphicsConfiguration
将为 Graphics2D
的目标(Component 或 Image)指定默认转换,此默认转换将用户空间坐标系映射到屏幕和打印机设备坐标,使原点映射到设备目标区域的左上角,并将 X 坐标轴向右方延伸,将 Y 坐标轴向下方延伸。对于接近 72 dpi 的设备(例如屏幕设备),默认转换的缩放比例设置为 1:1。对于高分辨率设备(例如打印机),默认转换的缩放比例设置为每平方英寸大约 72 个用户空间坐标。对于图像缓冲区,默认转换为 Identity
转换。
呈现过程
呈现过程可以分为四个阶段,这四个阶段由 Graphics2D
呈现属性控制。呈现器可以优化当中的许多步骤:可以缓存结果以用于未来调用;可以将多个虚拟步骤合成一个操作;可以将多种属性识别为共用的简单情况(可以通过修改操作的其他部分来消除各种属性间的差别)。
呈现过程中的步骤有:
- 确定呈现内容。
- 将呈现操作限制在当前
Clip
。Clip
由用户空间中的 Shape 指定,由使用Graphics
和Graphics2D
中各种 clip 操作方法的程序控制。此用户剪贴区 由当前Transform
转换到设备空间中,并与设备剪贴区 合并,后者是通过窗口可见性和设备范围定义的。用户剪贴区和设备剪贴区的组合定义复合剪贴区,复合剪贴区确定最终的剪贴区域。呈现系统不能修改用户剪贴区来反映得到的复合剪贴区。 - 确定呈现的颜色。
- 使用
Graphics2D
上下文中当前的 Composite 属性将颜色应用于目标绘图面。
三种类型的呈现操作,以及各自特殊呈现过程的细节如下:
- Shape 操作
- 如果该操作为
draw(Shape)
操作,则Graphics2D
上下文中当前 Stroke 属性上的 createStrokedShape 方法将用于构造包含指定Shape
轮廓的新Shape
对象。 - 使用
Graphics2D
上下文中的当前Transform
将Shape
从用户空间转换到设备空间。 Shape
的轮廓是通过使用Shape
的 getPathIterator 方法提取的,该方法返回一个沿着Shape
边界迭代得到的 PathIterator 对象。- 如果
Graphics2D
对象无法处理PathIterator
对象返回的曲线段,则可以调用Shape
的 getPathIterator 替代方法,该方法可使Shape
变得平滑。 - 对于 PaintContext,需要
Graphics2D
上下文中的当前 Paint,它指定了在设备空间中呈现的颜色。
- 如果该操作为
- 文本操作
- 以下步骤用于确定呈现指定
String
所需的字形集:- 如果参数是
String
,则要求Graphics2D
上下文中的当前Font
将String
中的 Unicode 字符转换为一个字形集,以表现 Font 实现的基本布局和成形算法。 - 如果参数是 AttributedCharacterIterator,则要求迭代器使用其内嵌的字体属性将其自身转换为 TextLayout。
TextLayout
实现更为复杂的字形布局算法,用于为不同书写方向的多种字体自动执行 Unicode 双方向布局调整。 - 如果参数是 GlyphVector,则
GlyphVector
对象已经包含了特定于字体的合适字形代码和每个字形位置的显式坐标。
- 如果参数是
- 查询当前的
Font
以获取指定字形的轮廓。这些轮廓被视为用户空间中相对于步骤 1 中确定的每个字形位置的形状。 - 字符轮廓按上面 Shape 操作下指示的方式填充。
- 查询当前
PaintContext
以获取Paint
,Paint 指定了设备空间中呈现的颜色。
- 以下步骤用于确定呈现指定
- Image 操作
- 感兴趣区域由源
Image
的边界框定义。此边界框在图像空间中指定,该空间即Image
对象的本地坐标系。 - 如果
AffineTransform
被传递到 drawImage(Image, AffineTransform, ImageObserver),则使用AffineTransform
将边界框从图像空间转换到用户空间。如果未提供AffineTransform
,则认为边界框已存在于用户空间中。 - 使用当前
Transform
将Image
的边界框从用户空间转换到设备空间。注意,转换边界框的结果不一定会得到设备空间中的矩形区域。 Image
对象确定要呈现的颜色,并根据当前Transform
和可选图像转换所指定的源到目标坐标映射关系进行采样。
- 感兴趣区域由源
默认呈现属性
Graphics2D
呈现属性的默认值有:
Paint
Component
的颜色。Font
Component
的Font
。Stroke
- 线宽为 1 的方形画笔,没有虚线、斜角线段接合和方形端点。
Transform
- 用于
Component
的GraphicsConfiguration
的 getDefaultTransform。 Composite
- AlphaComposite.SRC_OVER 规则。
Clip
- 不呈现
Clip
,输出局限于Component
。
呈现兼容性问题
JDK(tm) 1.1 呈现模型是基于像素化的模型,该模型的坐标无限细分,且位于像素之间。使用一个一像素宽的画笔执行绘制操作,填充路径锚点向下和向右的像素。JDK 1.1 呈现模型与大多数现有平台呈现类的功能一致,需要将整数坐标解析为离散的画笔,使其完全落在指定的像素成员上。
Java 2D(tm)(Java(tm) 2 平台)API 支持抗锯齿呈现器。一像素宽的画笔不需要完全落在像素 N 或像素 N+1 上。该画笔可以部分落在这两个像素上。不需要为宽画笔选择一个偏离方向,因为沿画笔遍历边缘发生的混合可让画笔的子像素位置对用户可见。另一方面,如果通过将 KEY_ANTIALIASING 提示键设置为 VALUE_ANTIALIAS_OFF 提示值而关闭了抗锯齿,则当画笔跨在像素边界上时,呈现器可能需要应用偏离来确定要修改哪个像素,例如在设备空间中,当画笔沿着整数坐标绘制时。虽然抗锯齿呈现器的功能使之不再需要呈现模型为画笔指定一个偏离,但对于在屏幕上绘制一像素宽的水平线和垂直线这种常见情形,还是需要抗锯齿和非抗锯齿呈现器执行类似的操作。为了确保通过将 KEY_ANTIALIASING 提示键设置为 VALUE_ANTIALIAS_ON 而打开的抗锯齿不会导致这些线突然变为此宽度的二倍或一半不透明,需要让该模型为这些线指定一个路径,使它们完全覆盖特定的像素集,以帮助提高其平滑性。
Java 2D API 维持与 JDK 1.1 呈现行为的兼容性,遗留操作和现有呈现器行为在 Java 2D API 下没有改变。定义了映射到常规 draw
和 fill
方法的遗留方法,它明确指示Graphics2D
根据 Stroke
和 Transform
属性以及呈现提示的设置扩展 Graphics
的方式。在默认属性设置下该定义的执行方式完全相同。例如,默认 Stroke
是一个宽度为 1 且没有虚线的 BasicStroke
,屏幕绘制的默认 Transform 是 Identity 转换。
下面两个规则提供了可预见的呈现行为(无论是否使用了重叠还是抗锯齿)。
- 将设备坐标定义为在设备像素之间,这避免了重叠呈现和抗锯齿呈现的结果不一致。如果将坐标定义为在像素的中心,则由矩形等形状覆盖的某些像素仅是半覆盖。通过重叠的呈现,半覆盖的像素可能呈现在形状内部,也可能呈现在形状外部。使用抗锯齿呈现,整个形状边缘上的像素都是半覆盖的。另一方面,由于坐标被定义在像素之间,所以无论是否使用抗锯齿进行呈现,像矩形这样的形状将不会有半覆盖像素。
- 使用
BasicStroke
对象勾画的线和路径可以“标准化”,从而在不同的可绘制点上定位时,无论使用重叠还是抗锯齿呈现进行的绘制都能提供一致的轮廓呈现。此标准化过程通过 KEY_STROKE_CONTROL 提示控制。虽然未指定准确的标准化算法,但此标准化的目标是为了确保可以使用一致的可视外观呈现线条,而不考虑它们在像素网格上的位置;另一个目的是促进以抗锯齿模式呈现更连续的水平和垂直线,从而与没有抗锯齿的线更为相似。典型的标准化步骤可以将抗锯齿线端点提升到像素中心,以减少混合量;也可以调整无抗锯齿线的子像素位置,以便浮点线宽度舍入为近似相等的偶数或奇数像素计数。此过程可以将端点向上移动半个像素(通常沿两个坐标轴的正无穷大方向移动),以得到一致的结果。
在默认属性设置下,以下定义的常规遗留方法与以前指定行为的执行方式完全相同:
- 对于
fill
操作,包括fillRect
、fillRoundRect
、fillOval
、fillArc
、fillPolygon
和clearRect
,现在可以使用所需的Shape
调用 fill。例如,在填充矩形时可调用:fill(new Rectangle(x, y, w, h));
- 类似地,对于绘制操作,包括
drawLine
、drawRect
、drawRoundRect
、drawOval
、drawArc
、drawPolyline
和drawPolygon
,现在可以使用所需的Shape
调用 draw。例如,在绘制矩形时可调用:draw(new Rectangle(x, y, w, h));
draw3DRect
和fill3DRect
方法是根据Graphics
类中的drawLine
和fillRect
方法实现的,根据Graphics2D
上下文中的当前Stroke
和Paint
对象可以预知其行为。此类重写了那些独占地使用当前Color
的实现,重写当前Paint
以及使用fillRect
的Paint
,以描述与以前存在的方法完全相同的行为,而不考虑当前Stroke
的设置。
Graphics
类仅定义了 setColor
方法来控制要绘制的颜色。由于 Java 2D API 扩展了 Color
对象来实现新的 Paint
接口,因此现有的 setColor
方法现在是将当前 Paint
属性设置为 Color
对象的便捷方法。setColor(c)
等同于 setPaint(c)
。
Graphics
类定义了两种方法来控制如何将颜色应用到目标。
setPaintMode
方法实现为设置默认Composite
的便捷方法,它等同于setComposite(new AlphaComposite.SrcOver)
。setXORMode(Color xorcolor)
方法实现为设置特殊Composite
对象的便捷方法,它忽略源颜色的Alpha
分量,并将目标颜色设置为以下值:dstpixel = (PixelOf(srccolor) ^ PixelOf(xorcolor) ^ dstpixel);
package com.mapbar.graphics; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; /** * Class DrawGraphics.java * Description java2D绘制直线,矩形,椭圆,旋转图形 * Company mapbar * author Chenll * Version 1.0 * Date 2012-7-20 下午12:06:15 */ public class DrawGraphics{ private BufferedImage image; private Graphics2D graphics; public void init(){ int width=480,hight=720; image = new BufferedImage(width,hight,BufferedImage.TYPE_INT_RGB); //获取图形上下文 graphics = (Graphics2D)image.getGraphics(); } /** * 创建一个(x1,y1)到(x2,y2)的Line2D对象 * @throws IOException */ public void drawLine() throws IOException{ init(); Line2D line=new Line2D.Double(2,2,300,300); graphics.draw(line); graphics.dispose(); outImage("PNG","D:\Line.PNG"); } /** * 创建一个左上角坐标是(50,50),宽是300,高是400的一个矩形对象 * @throws IOException */ public void drawRect() throws IOException{ init(); Rectangle2D rect = new Rectangle2D.Double(50,50,400,400); graphics.draw(rect); graphics.fill(rect); graphics.dispose(); outImage("PNG","D:\Rect.PNG"); } /** * 创建了一个左上角坐标是(50,50),宽是300,高是200的一个椭圆对象,如果高,宽一样,则是一个标准的圆 * * @throws IOException */ public void drawEllipse() throws IOException{ init(); Ellipse2D ellipse=new Ellipse2D.Double(50,50,300,200); graphics.draw(ellipse); graphics.fill(ellipse); graphics.dispose(); outImage("PNG","D:\ellipse.PNG"); } /** * 输出绘制的图形 * @param type * @param filePath * @throws IOException */ public void outImage(String type,String filePath) throws IOException{ ImageIO.write(image,type, new File(filePath)); } public static void main(String[] args) throws IOException{ DrawGraphics dg = new DrawGraphics(); dg.drawLine(); dg.drawRect(); dg.drawEllipse(); } }