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);
  • 相关阅读:
    欧拉回路 定理
    UESTC 1087 【二分查找】
    POJ 3159 【朴素的差分约束】
    ZOJ 1232 【灵活运用FLOYD】 【图DP】
    POJ 3013 【需要一点点思维...】【乘法分配率】
    POJ 2502 【思维是朴素的最短路 卡输入和建图】
    POJ 2240 【这题貌似可以直接FLOYD 屌丝用SPFA通过枚举找正权值环 顺便学了下map】
    POJ 1860【求解是否存在权值为正的环 屌丝做的第一道权值需要计算的题 想喊一声SPFA万岁】
    POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】
    js 实现slider封装
  • 原文地址:https://www.cnblogs.com/lmsthoughts/p/11519226.html
Copyright © 2011-2022 走看看