zoukankan      html  css  js  c++  java
  • java绘图(基于Graphics2D)

    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);
  • 相关阅读:
    【WCF】无废话WCF入门教程
    【IIS8】在IIS8添加WCF服务支持
    iOS 中如何将View设置为圆角的矩形?
    在iOS开发中使用FMDB
    iOS中FMDB的使用【单例】
    普通分页笔记
    基础BaseDao
    连接池技术 实现的四个要素:jdbc.properties配置- 读取配置的单例类 --ConfigManage--BaseDao写法
    反射生成对象,调用对象方法
    context分页
  • 原文地址:https://www.cnblogs.com/lmsthoughts/p/11519226.html
Copyright © 2011-2022 走看看