Java 利用 Thumbnailator 合成图片实例

一直以来 Thumbnailator 都是图片压缩、制作缩略图的利器,也是一直在更新,最近一次的更新也是在2022年的1月初。(https://github.com/coobird/thumbnailator

最近项目中遇到一个分享的功能,需要后台合成一张图片,图片除了本身的模板之外,还有一个用户头像、文字、二维码、和一些其他信息,就像下面这张图。

file

本身项目中也是用到了 Thumbnailator,后来就想着减少引入包,看看它有没有合成的功能。一顿Google猛如虎,没有明确的找到图片合成,但是留意到一个关键字,就是“水印”,一般的水印都是带透明度的,要是我把水印的透明度设置成 0%,不就好了,开干!~

最终代码如下:


//GroupBuyInfo 这是我获取信息的自定义的Bean
    public static BufferedImage makePoster(GroupBuyInfo gbi){
        //待返回的 BufferedImage对象,如果你想返回其他的也很方便
        BufferedImage poster = null;

        //这两行无关代码,忽略不计
        String token = APISessionContext.getToken(); //获取当前用户的token
        String adfontesAppId = getBean(IPartyTokenService.class).findTopOne(new GenericQueryWrapper<PartyToken>().eq("tokenStr",token)).getAdfontesAppId();//根据当前用户拿到appid

        //拿到发起人的信息
        PartyAppExt pae = getBean(PartyAppExtServiceImpl.class).findTopOne(new GenericQueryWrapper<PartyAppExt>().eq("adfontesAppId",adfontesAppId).eq("partyId",gbi.getPartyId()));

        //海报尺寸 //本身的模板的尺寸,这个是固定值
        int width = 634;
        int height = 1084;

        //二维码尺寸,长宽相等
        int qrcodeWidth = 120;

        //头像尺寸,长宽相等
        int headImgWidth = 70;

        //团长icon标尺寸
        int headFlagWidth = 68;

        try {
            //这里制作一个文字图片,因为文字有特殊的字体和字号
            BufferedImage textIMg = getTextMark4Poster(pae==null?"小幸运":pae.getNickname(),gbi.getNumberOfFulfill().intValue());
            //获取文字图片的宽度
            int textWidth = textIMg.getWidth();

            //这里获取一些模板、二维码、头像
            //目前是测试,发布后头像和二维码应该是外部获取的
            //因为是springboot 打jar包发布的,所以是这样获取jar包里的图片
            Resource resource4HeadFlag = new PathMatchingResourcePatternResolver().getResource("images" + File.separator + "headFlag.png");
            Resource resource4qrcode = new PathMatchingResourcePatternResolver().getResource("images" + File.separator + "qrcode.png");
            Resource resource4Template = new PathMatchingResourcePatternResolver().getResource("images" + File.separator + "groupBuyTemplate.png");

            return Thumbnails.of(resource4Template.getInputStream()) //模板大图
                    .watermark(new FreePositionsThumbnails(width/2-qrcodeWidth/2, 800), ImageIO.read(resource4qrcode.getInputStream()), 1f) //读取二维码,并且设置在指定的位置
                    .watermark(new FreePositionsThumbnails(width/2-textWidth/2,715),textIMg,1f) //设置文字
                    .watermark(new FreePositionsThumbnails(width/2-headImgWidth/2,630),getheadImg4Poster(pae.getAvatar()),1f) //设置头像
                    .watermark(new FreePositionsThumbnails(width/2-headFlagWidth/2,630+45),ImageIO.read(resource4HeadFlag.getInputStream()),1f) //设置icon
                    .scale(1.0f) //压缩比
                    .asBufferedImage(); //转成图片
                    //.toFile("/home/wang/Downloads/newfile.png");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FontFormatException e) {
            e.printStackTrace();
        }

        return poster;
    }
/**
     * 绘制昵称和标题的图片
     * @param nickName
     * @param numberOfFulfill
     * @return
     * @throws IOException
     * @throws FontFormatException
     */
    private static BufferedImage getTextMark4Poster(String nickName,int numberOfFulfill) throws IOException, FontFormatException {
        String text = StringUtils.format("{}的{}人团",nickName,numberOfFulfill);
        //先绘制一张空白文字的图片,用于计算文字的大小
        BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();

        Resource r = new PathMatchingResourcePatternResolver().getResource("fonts" + File.separator + "SourceHanSansCN-Medium.otf");

        Font font = Font.createFont(Font.TRUETYPE_FONT, r.getInputStream()).deriveFont(24f);

        //Font font = new Font("Arial", Font.PLAIN, 12);
        g2d.setFont(font);
        FontMetrics fm = g2d.getFontMetrics();
        int width = fm.stringWidth(text);
        int height = fm.getHeight();
        g2d.dispose();

        //这张图片是最终生成的文字图片
        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        g2d = img.createGraphics();
        //g2d.setColor(Color.RED);
        //g2d.fillRect(0, 0, width, height);

        //KEY_ALPHA_INTERPOLATION  代表 alpha 合成微调的键,该微调可能的值有 VALUE_ALPHA_INTERPOLATION_SPEED(追求速度)、VALUE_ALPHA_INTERPOLATION_QUALITY(追求质量)和VALUE_ALPHA_INTERPOLATION_DEFAULT,代表平台缺省值。
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);

        //KEY_ANTIALIASING 决定是否使用抗锯齿。当着色有倾斜角度的线时,通常会得到一组阶梯式的像素排列,使这条线看上去不平滑,经常被称为 锯齿状图形。
        // 抗锯齿是一种技术,它设置有倾斜角度的线的像素亮度,以使线看起来更平滑。因此,这个微调是用来决定在着色有倾斜角度的线时是否在减少锯齿状图形上花费时间。
        // 可能的值有 VALUE_ANTIALIAS_ON(使用抗锯齿)、VALUE_ANTIALIAS_OFF(不使用抗锯齿) 和 VALUE_ANTIALIAS_DEFAULT(默认的抗锯齿)。
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

        //KEY_COLOR_RENDERING 控制颜色着色的渲染方式。可能的值有 VALUE_COLOR_RENDER_SPEED(追求速度)VALUE_COLOR_RENDER _QUALITY(追求质量) 和 VALUE_COLOR_RENDER_DEFAULT(默认)。
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_QUALITY);

        //KEY_DITHERING 控制如何处理抖动。抖动是用一组有限的颜色合成出一个更大范围的颜色的过程,方法是给相邻像素着色以产生不在该组颜色中的新的颜色幻觉。可能的值有 VALUE_DITHER_ENABLE(不抖动)、VALUE_DITHER _DISABLE (抖动)和 VALUE_DITHER_DEFAULT(默认)。
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_ENABLE);

        //KEY_FRACTIONALMETRICS  字体规格。可能的值有 VALUE_FRACTIONALMETRICS_ON(启用字体规格)、VALUE_FRACTIONALMETRICS_OFF(禁用字体规格) 和VALUE_FRACTIONALMETRICS _DEFAULT(默认)。
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

        //KEY_RENDERING 确定着色技术,在速度和质量之间进行权衡。可能的值有 VALUE_RENDERING_SPEED(追求速度)、VALUE_RENDERING _QUALITY(追求质量) 和VALUE_RENDERING_DEFAULT(默认)。
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

        //KEY_STROKE_CONTROL 笔划规范化控制,可能有的值有VALUE_STROKE_NORMALIZE、VALUE_STROKE_PURE和VALUE_STROKE_DEFAULT
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        g2d.setFont(font);
        fm = g2d.getFontMetrics();
        g2d.setColor(Color.BLACK);
        g2d.drawString(text, 0, fm.getAscent());
        g2d.dispose();

        return img;

    }

另外,watermark 水印方法中,第一个参数是位置(需要实现 Position 接口的实现类),自带的枚举类 net.coobird.thumbnailator.geometry.Positions 有如下几个,TOP_LEFT、TOP_CENTER、CENTER、TOP_RIGHT 等等,看名字也知道什么意思,但是如果需要自定义就不行,至少我没找到好办法,所以我就自己写一个,实现接口即可,其实原理是一样的,那些枚举只是代替你计算好了一些位置,什么坐上,居中之类的。

public class FreePositionsThumbnails implements Position {

    public int x;
    public int y;

    public FreePositionsThumbnails(int x, int y){
        this.x = x;
        this.y = y;
    }

    @Override
    public Point calculate(int enclosingWidth, int enclosingHeight, int width, int height, int insetLeft, int insetRight, int insetTop, int insetBottom) {
        return new Point(x,y);
    }
}

站内相关文章:

Comment ()
如果您有不同的看法,或者疑问,欢迎指教