1.绘图基本操作
请参考下面基础示例:
1 int width = 200, height = 250;
2 //创建图片对象
3 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
4 //基于图片对象打开绘图
5 Graphics2D graphics = image.createGraphics();
6 //绘图逻辑 START (基于业务逻辑进行绘图处理)……
7
8 //绘制圆形
9 graphics.setColor(Color.BLACK);
10 Ellipse2D.Double ellipse = new Ellipse2D.Double(20, 20, 100, 100);
11 graphics.draw(ellipse);
12
13 // 绘图逻辑 END
14 //处理绘图
15 graphics.dispose();
16 //将绘制好的图片写入到图片
17 ImageIO.write(image, "png", new File("abc.png"));
如上代码所示,使用java绘图基本操作流程如下:
a.得到一个 BufferedImage ,可以是直接指定分辨率new一个空图片,也
b.基于此BufferedImage 创建一个绘图对象,使用 createGraphics 方法,得 Graphics2D 实例
c.使用Graphics2D 实例进行画图,所有绘图坐标基于创建此Graphics2D 的BufferedImage。示例中在图片上画了一个圆形。
d.调用Graphics2D 对象的 dispose() 方法,进行绘图处理,使绘图效果应用到BufferedImage 对象
e.使用ImageIO类的write函数将图片对象转换到文件或二进制流
上面是使用java进行绘图的基本流程,其中步骤c涉及到很多绘图细节。下面总结一些常用绘图操作:
限制绘图区域
如果绘图时,只希望指定范围内的区域绘图生效,其他区域不受影响,我们可以使用 setClip 函数Shape区域对象作为参数设置剪辑区域
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, diameter, diameter);
Graphics2D graphics = formatAvatarImage.createGraphics();
//设置剪辑区域,范围外的区域不受绘图影响。这里指定圆形区域作为绘制区域
graphics.setClip(shape);
// 将另一张图片绘制到图像中,
graphics.drawImage(avatarImage, (diameter - width + circleW) / 2, (diameter - height + circleW) / 2, null);
graphics.dispose();
图形的原点坐标:
由于绘制图形时,所有绘制都基于坐标进行绘制,所以绘制之前必须了解图形的原点坐标,才能将图形绘制到准确的位置
1.方形:方形的原点坐标位于左上角(图片本身都是方形,因此在图像中绘制另一张图片时,图片的原点坐标也在左上角)
2.椭圆形: 椭圆形的原点坐标位于圆心
3.文字:文字的原点坐标位于左侧与基线的交点上(参考后面绘制文字)
绘制图片
图片绘制主要分三步
1.将图片转换为BufferedImage 对象
2.计算绘制位置
3.进行绘制
1 //将图片转换为BufferedImage对象
2 BufferedImage bufferedImage = ImageIO.read(new File("C:\\Users\\minggliu\\Pictures\\test\\test.png"));
3 //计算绘制位置
4 int x = 0, y = 0;
5 //执行绘制
6 //graphics.drawImage(bufferedImage.getScaledInstance(imageW, imageH, Image.SCALE_DEFAULT),100, 100, null);
7 //graphics.drawImage(bufferedImage, 0, 0, width / 2, height / 2, null);
8 graphics.drawImage(bufferedImage, x, y, null);
注意到上面执行绘制注释了上面两种,
a.使用
graphics.drawImage(bufferedImage, x, y, null);
绘制图片不会进行resize
b.使用
graphics.drawImage(bufferedImage, 0, 0, width / 2, height / 2, null);
绘制图片,系统会将图片resize到 width / 2, height / 2 大小
c.使用
graphics.drawImage(bufferedImage.getScaledInstance(imageW, imageH, Image.SCALE_DEFAULT),100, 100, null);
绘制图片则先进行了resize,再画到对应的位置,结果与b相同,不过预先的resize有更多的功能,可以指定resize方式
对于一些特殊resize要求,需要走代码逻辑进行特殊处理,下面给出常用resize方式:
以图形中间为原点进行无压缩裁剪resize:
public static BufferedImage resize(BufferedImage image, int needW, int needH) {
int resizeW, resizeH, resizeX, resizeY;
int imgW = image.getWidth(), imgH = image.getHeight();
int wantH = imgW * needH / needW;
//如果图片属于过方形
if (wantH < imgH) {
resizeH = imgW * needH / needW;
resizeW = imgW;
resizeX = 0;
resizeY = (imgH - wantH) / 2;
}
//图片属于过长形
else {
resizeH = imgH;
resizeW = imgH * needW / needH;
resizeX = (imgW - imgH * needW / needH) / 2;
resizeY = 0;
}
return image.getSubimage(resizeX, resizeY, resizeW, resizeH);
}
绘制文字
1.绘制文字需要指定字体,同时需要系统运行的系统上安装了对应字体
安装:
a.将字体文件存放到字体目录
/usr/share/fonts
b.刷新字体信息
sudo mkfontscale #生成核心字体信息
sudo mkfontdir
sudo fc-cache -fv
c.重启进程
3.文本框的坐标定位
文字的原点坐标不同于一般图形在右上角,如果需要将文字准确的绘制到指定位置,需要了解文字的原点坐标位置。
如下图所示,文本框的原点x坐标位于文本框最左侧,y坐标则位于基线上。基线以上为asent,基线以下为decent.
为什么会有基线这个概念,可能会让人产生疑惑,实际上是有原因的。实际上基线相同的文字,即使字体大小不一样,绘制出来的结果是文本底部对齐的。 因为文字底部还有一部分留白,因此文字底部对齐不是基于底部坐标对齐,而是进行基线对齐。
如下图所示,为文字的绘制示例, java提供了api获取文本框的长宽高,asent、decent等,我们需要基于字体Font得到 FontMetrics ,基于FontMetrics 我们就能获取字体高度、asent、decent,结合具体的字符串得到宽度(所以文本绘制实际上不支持换行)。
//指定字体
final Font logoFont = new Font("PingFang SC", Font.BOLD, 50);
FontMetrics metrics = graphics.getFontMetrics(logoFont);
//指定要绘制的字符串
String amount = String.valueOf(coupon.getAmount());
//得到字符串绘制宽度
int logoW = metrics.stringWidth(amount);
//计算绘制原点坐标,
//文本最左边位于x轴logoX
int logoX = xStart;
//文本基于logoH 居中对齐
int logoH = hStart + metrics.getHeight() / 2 - metrics.getDescent();
//绘制文本框
graphics.setFont(timeFont);
graphics.setPaint(timeColor);
graphics.drawString(amount, logoX, logoH);