java 生成分享图片(二维码,图片拼接,文字编辑)

2023-11-09 00:20

本文主要是介绍java 生成分享图片(二维码,图片拼接,文字编辑),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

需求背景

之前遇到过需要后端,根据每个用户生成带背景宣传图带二维码带用户图片带文字的合成图片的需求,自己当时花了半天的时间整理了资料,今天把自己写的代码分享出来,如果有同样需求的人,希望能给个好评,有其他建议的童鞋,可以一起讨论交流。

文章下面贴有项目地址,感谢star
项目整合了二维码和日志,有需要的童鞋也可以做参考

图片缓冲类 BufferedImage

BufferedImage类是Image的实现类,是可以把图片加载到内存的缓冲类,我写的代码中就基本上都是基于该类实现对图片的操作。

图片加载 Thumbnails

Thumbnails是谷歌开源的一套图片工具类
当我需要把本地图片加载进来得到BufferedImage对象的时候,我们可以通过

		Thumbnails.of(filePath).scale(1).asBufferedImage();

如果是远程图片

		Thumbnails.of(new URL(filePath)).scale(1).asBufferedImage();

添加文本

	/*** 修改图片,返回修改后的图片缓冲区(只输出一行文本)*/public BufferedImage modifyImage(BufferedImage img, Object content, int x, int y) {try {int w = img.getWidth();int h = img.getHeight();g = img.createGraphics();g.setBackground(Color.WHITE);g.setColor(Color.BLACK);//设置字体颜色if (this.font != null) {g.setFont(this.font);}// 验证输出位置的纵坐标和横坐标if (x >= h || y >= w) {this.x = h - this.fontsize + 2;this.y = w;} else {this.x = x;this.y = y;}g.setFont( new Font("微软雅黑", Font.BOLD, 22));if (content != null) {g.drawString(content.toString(), this.x, this.y);}g.setBackground(Color.WHITE);g.dispose();} catch (Exception e) {System.out.println(e.getMessage());}return img;}

生成图片到本地

ImageIO提供了write的方法,将图片文件打印到本地

	/*** 生成新图片到本地*/public boolean writeImageLocal(String newImage, BufferedImage img) {boolean isok = false;if (newImage != null && img != null) {try {File outputfile = new File(newImage);isok = ImageIO.write(img, "jpg", outputfile);} catch (IOException e) {System.out.println(e.getMessage());}}return isok;}

工具类代码

有了上面四个点,我们基本上就可以图片为所欲为了

package com.xwn.utils;import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.net.URL;import java.awt.Rectangle;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;import com.xwn.constant.Constant;
import com.xwn.constant.PicConstant;
import com.xwn.model.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;import net.coobird.thumbnailator.Thumbnails;import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;/*** 服务端画图* 绘制,拼接,修改图片* @author xwn*/
public class PictureUtils {private static final Logger log = LoggerFactory.getLogger(PictureUtils.class);private Font font = new Font("微软雅黑", Font.BOLD, 70);// 添加字体的属性设置private Graphics2D g = null;private int fontsize = 0;private int x = 0;private int y = 0;/*** 导入本地图片到缓冲区*/public BufferedImage loadImageLocal(String imgName) {try {return ImageIO.read(new File(imgName));} catch (IOException e) {System.out.println(e.getMessage());}return null;}/*** 导入网络图片到缓冲区*/public BufferedImage loadImageUrl(String imgName) {try {URL url = new URL(imgName);return ImageIO.read(url);} catch (IOException e) {System.out.println(e.getMessage());}return null;}/*** 生成新图片到本地*/public boolean writeImageLocal(String newImage, BufferedImage img) {boolean isok = false;if (newImage != null && img != null) {try {File outputfile = new File(newImage);isok = ImageIO.write(img, "jpg", outputfile);} catch (IOException e) {System.out.println(e.getMessage());}}return isok;}/*** 将{@link BufferedImage}生成formatName指定格式的图像数据* @param source* @param formatName 图像格式名,图像格式名错误则抛出异常* @return*/public static void wirteBytes(BufferedImage source,String formatName,String newImage){Assert.notNull(source, "source");ByteArrayOutputStream output = new ByteArrayOutputStream();Graphics2D g = null;try {for(BufferedImage s=source;!ImageIO.write(s, formatName, output);){if(null!=g) {throw new IllegalArgumentException(String.format("not found writer for '%s'",formatName));}s = new BufferedImage(source.getWidth(),source.getHeight(), BufferedImage.TYPE_INT_RGB);g = s.createGraphics();g.drawImage(source, 0, 0,null);}} catch (IOException e) {throw new RuntimeException(e);} finally {if (null != g) {g.dispose();}}}/*** 将指定颜色变透明 只能保存 png jpg* @param imageSrc 图片* @param mask 颜色* @return*/public static BufferedImage createImageByMaskColorEx(BufferedImage imageSrc, Color mask) {int x, y;x = imageSrc.getWidth(null);y = imageSrc.getHeight(null);BufferedImage imageDes = new BufferedImage(x, y,BufferedImage.TYPE_4BYTE_ABGR);WritableRaster rasterDes = imageDes.getRaster();int[] des = new int[4];while (--x >= 0) {for (int j = 0; j < y; ++j) {int rgb = imageSrc.getRGB(x, j);int sr, sg, sb;sr = (rgb & 0xFF0000) >> 16;sg = (rgb & 0xFF00) >> 8;sb = rgb & 0xFF;if (sr == mask.getRed() && sg == mask.getGreen()&& sb == mask.getBlue()) {des[3] = 0;} else {des[0] = sr;des[1] = sg;des[2] = sb;des[3] = 255;}rasterDes.setPixel(x, j, des);}}return imageDes;}/*** 设置颜色* @param img 图片* @return*/public BufferedImage setColor(BufferedImage img){//定义一个数组,存放RGB值int[] rgb = new int[3];int width = img.getWidth();int height = img.getHeight();int minx = img.getMinTileX();int miny = img.getMinTileY();//遍历像素点,判断是否更换颜色for (int i = minx; i < width; i++) {for (int j = miny; j < height; j++) {//换色int pixel = img.getRGB(i, j);rgb[0] = (pixel & 0xff0000) >>16;rgb[1] = (pixel & 0xff00) >>8;rgb[2] = (pixel & 0xff) ;if (rgb[0]<230&&rgb[0]>100&& rgb[1]<230&&rgb[1]>100 && rgb[2]<230&&rgb[2]>100) {img.setRGB(i, j, 0xffffff);}}}return img;}/*** 设定文字的字体等* @param fontStyle 文字字体* @param fontSize 大小*/public void setFont(String fontStyle, int fontSize) {this.fontsize = fontSize;this.font = new Font(fontStyle, Font.PLAIN, fontSize);}/*** 修改图片,返回修改后的图片缓冲区(只输出一行文本)*/public BufferedImage modifyImage(BufferedImage img, Object content, int x, int y) {try {int w = img.getWidth();int h = img.getHeight();g = img.createGraphics();g.setBackground(Color.WHITE);g.setColor(Color.BLACK);//设置字体颜色if (this.font != null) {g.setFont(this.font);}// 验证输出位置的纵坐标和横坐标if (x >= h || y >= w) {this.x = h - this.fontsize + 2;this.y = w;} else {this.x = x;this.y = y;}g.setFont( new Font("微软雅黑", Font.BOLD, 22));if (content != null) {g.drawString(content.toString(), this.x, this.y);}g.setBackground(Color.WHITE);g.dispose();} catch (Exception e) {System.out.println(e.getMessage());}return img;}/*** 修改图片,返回修改后的图片缓冲区(只输出一行文本)* @param img 图片* @return*/public BufferedImage modifyImageYe(BufferedImage img) {try {int w = img.getWidth();int h = img.getHeight();g = img.createGraphics();g.setBackground(Color.WHITE);g.setColor(Color.blue);//设置字体颜色if (this.font != null)g.setFont(this.font);g.drawString("reyo.cn", w - 85, h - 5);g.dispose();} catch (Exception e) {System.out.println(e.getMessage());}return img;}/*** 合成图片* @param b 图片b* @param d 图片d* @param x x坐标* @param y y坐标* @param height 高度* @param weight 宽度* @return*/public BufferedImage modifyImagetogeter(BufferedImage b, BufferedImage d,int x,int y,int height ,int weight) {try {g = d.createGraphics();g.drawImage(b, x, y, weight, height, null);g.dispose();} catch (Exception e) {System.out.println(e.getMessage());}return d;}/*** 对图片进行剪裁   返回字节数组* @param is            图片输入流* @param width            裁剪图片的宽* @param height        裁剪图片的高* @param imageFormat    输出图片的格式 "jpeg jpg等"* @return*/public static byte[] clipImage(InputStream is,int width, int height, String imageFormat){ByteArrayOutputStream bos = new ByteArrayOutputStream();try {// 构造Image对象BufferedImage src = javax.imageio.ImageIO.read(is);// 缩小边长BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 绘制 缩小  后的图片tag.getGraphics().drawImage(src, 0, 0, width, height, null);ImageIO.write(tag, imageFormat, bos);} catch (IOException e) {e.printStackTrace();}return bos.toByteArray();}/*** 对图片裁剪,并把裁剪新图片保存* @param srcPath               读取源图片路径* @param toPath             写入图片路径* @param x                     剪切起始点x坐标* @param y                     剪切起始点y坐标* @param width                 剪切宽度* @param height             剪切高度* @param readImageFormat     读取图片格式* @param writeImageFormat   写入图片格式*/public static void cropImage(String srcPath, String toPath, int x,int y,int width,int height, String readImageFormat,String writeImageFormat){FileInputStream fis = null ;ImageInputStream iis =null ;try{//读取图片文件fis = new FileInputStream(srcPath);Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(readImageFormat);ImageReader reader = readers.next();//获取图片流iis = ImageIO.createImageInputStream(fis);reader.setInput(iis, true);ImageReadParam param = reader.getDefaultReadParam();//定义一个矩形Rectangle rect = new Rectangle(x, y, width, height);//提供一个 BufferedImage,将其用作解码像素数据的目标。param.setSourceRegion(rect);BufferedImage bi = reader.read(0, param);//保存新图片ImageIO.write(bi, writeImageFormat, new File(toPath));}catch(Exception e){e.printStackTrace();}finally{try{if(fis!=null){fis.close();}if(iis!=null){iis.close();}}catch(Exception e){e.printStackTrace();}}}/*** 传入的图像必须是正方形的 才会 圆形 如果是长方形的比例则会变成椭圆的* @param bi1 图片* @return* @throws IOException*/public static BufferedImage convertCircular(BufferedImage bi1) throws IOException {// 透明底的图片BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, bi1.getWidth(), bi1.getHeight());Graphics2D g2 = bi2.createGraphics();g2.setClip(shape);// 使用 setRenderingHint 设置抗锯齿g2.drawImage(bi1, 0, 0, null);// 设置颜色g2.setBackground(Color.green);g2.dispose();return bi2;}/*** 缩小Image,此方法返回源图像按给定宽度、高度限制下缩放后的图像* @param inputImage* @param newWidth:压缩后宽度* @param newHeight:压缩后高度* @param beijing:背景图片* @throws java.io.IOException*/public static BufferedImage scaleByPercentage(BufferedImage inputImage, int newWidth, int newHeight,BufferedImage beijing) throws Exception {// 获取原始图像透明度类型int type = inputImage.getColorModel().getTransparency();int width = inputImage.getWidth();int height = inputImage.getHeight();// 开启抗锯齿RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 使用高质量压缩renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);BufferedImage img = new BufferedImage(newWidth, newHeight, type);Graphics2D graphics2d = img.createGraphics();graphics2d.setRenderingHints(renderingHints);graphics2d.drawImage(inputImage, 0, 0, newWidth, newHeight, 0, 0, width, height, null);graphics2d.dispose();return img;}/*** 头像缩放 四舍五入  double转int* @param beijing_* @return*/public static Integer doubleToInt(BufferedImage beijing_,double d){return (new Double((d*beijing_.getHeight()))).intValue();}/*** 让字符串隔开展现* @param code* @return*/public static String getCode(String code){StringBuilder sb = new StringBuilder();for (int i = 0; i < code.length(); i++) {if(i != (code.length()-1)){sb.append(code.charAt(i)).append(" ");}else{sb.append(code.charAt(i));}}return sb.toString();}/*** 网络图片输出到BufferedImage* @throws IOException*/private BufferedImage getBufferedImageForURL(String filePath) throws IOException {//asBufferedImage() 返回BufferedImageBufferedImage thumbnail = Thumbnails.of(new URL(filePath)).scale(1).asBufferedImage();return thumbnail;}/*** 本地图片输出到BufferedImage* @throws IOException*/private BufferedImage getBufferedImageForLocal(String filePath) throws IOException {/*** asBufferedImage() 返回BufferedImage*/BufferedImage thumbnail = Thumbnails.of(filePath).scale(1).asBufferedImage();return thumbnail;}/*** 合成图片* @param user 用户* @throws IOException*/public static void initImage(UserInfo user) throws IOException {Map<String,String> map = new HashMap<String,String>();PictureUtils tt = new PictureUtils();String baseUrl = Constant.BASE_URL;String beijing = PicConstant.yaoqingbeijing128;String dibian = PicConstant.yaoqingdibian128;//底边合成背景String newPicUrl = new StringBuffer(baseUrl).append("ok/").append(user.getUserId()).append(".jpg").toString();if(!new File(newPicUrl).exists()){//获取绑定二维码图片Map<String,String> qrCodeMap = QrCodeUtil.getUserQrImgURL(user);if(qrCodeMap != null){String qrCodeImgURL = qrCodeMap.get("qrPath");String qrCode = qrCodeMap.get("qrCode");boolean isOk = true;try {BufferedImage head_ = tt.getBufferedImageForURL(user.getHeadImgUrl());BufferedImage qrCodeImg = tt.getBufferedImageForLocal(qrCodeImgURL);BufferedImage beijing_ = tt.getBufferedImageForURL(beijing);BufferedImage dibian_ = tt.getBufferedImageForURL(dibian);head_ = convertCircular(head_);head_ = scaleByPercentage(head_, doubleToInt(dibian_,PicConstant.DOUBLE_TO_INT_HEAD), doubleToInt(dibian_,PicConstant.DOUBLE_TO_INT_HEAD),beijing_);isOk = tt.writeImageLocal(new StringBuilder(baseUrl).append("head.jpg").toString(),head_);//合成头像int x = 30;int y = 75;int weight1 = head_.getWidth();int height1 = head_.getHeight();tt.modifyImagetogeter(head_, dibian_,x,y,height1,weight1);isOk = tt.writeImageLocal(new StringBuilder(baseUrl).append("dibian.jpg").toString(), tt.modifyImagetogeter(head_, dibian_,x,y,height1,weight1));//添加文字x = 100;y = 135;tt.modifyImage(dibian_,getCode(user.getInviteCode()),x,y);isOk = tt.writeImageLocal(new StringBuilder(baseUrl).append("xiabufen1.jpg").toString(),tt.modifyImage(dibian_,getCode(user.getInviteCode()),x,y));//合成二维码x = 325;y = 12;int weight3 = doubleToInt(qrCodeImg,PicConstant.DOUBLE_TO_INT_QR);int height3 = doubleToInt(qrCodeImg,PicConstant.DOUBLE_TO_INT_QR);tt.modifyImagetogeter(qrCodeImg, dibian_,x,y,doubleToInt(qrCodeImg,PicConstant.DOUBLE_TO_INT_QR),doubleToInt(qrCodeImg,PicConstant.DOUBLE_TO_INT_QR));isOk = tt.writeImageLocal(new StringBuilder(baseUrl).append("xiabufen2.jpg").toString(), tt.modifyImagetogeter(qrCodeImg, dibian_,x,y,height3,weight3));x = 0;y = beijing_.getHeight()-dibian_.getHeight();int weight = dibian_.getWidth();int height = dibian_.getHeight();isOk = tt.writeImageLocal(newPicUrl, tt.modifyImagetogeter(dibian_, beijing_,x,y,height,weight));} catch (Exception e) {log.error("合成"+ e.getMessage());}if(!isOk){log.error("生成图片失败!");}map.put("qrCode", qrCode);map.put("newPicUrl", newPicUrl);}}else{map.put("qrCode", new StringBuffer(Constant.DEFAULT_IMG_BASEURL).append(user.getUserId()).toString());map.put("newPicUrl", newPicUrl);}log.info(map.toString());}public static void main(String[] args) throws IOException {UserInfo user = new UserInfo();user.setUserId(1);user.setHeadImgUrl(PicConstant.Head_Test);user.setInviteCode("123456");initImage(user);}
}

这里我只放主工具类的代码,想要全部代码可以点击文章下面的码云地址下载项目。

效果图片

示例样本用到了我的对象存储,有兴趣的童鞋可以点击 阿里云COS

附带图片部分图片
完成的图片在这里插入图片描述

码云地址

https://gitee.com/love_xwn/csdn-space.git

这篇关于java 生成分享图片(二维码,图片拼接,文字编辑)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/373236

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文