java 实现图片高清缩放、合并、添加文字/图片水印

通用包引用:

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;

1、图片添加文字水印

/**
 * 添加图片水印
 * 
 * @param targetImg
 *            目标图片路径
 * @param waterImg
 *            水印图片路径
 * @param x
 *            水印图片距离目标图片左侧的偏移量,如果x<0, 则在正中间
 * @param y
 *            水印图片距离目标图片上侧的偏移量,如果y<0, 则在正中间
 * @param alpha
 *            透明度(0.0 -- 1.0, 0.0为完全透明,1.0为完全不透明)
 */
 
public final static void addImageWeatermark(String targetImg, String waterImg, int x, int y, float alpha) {
	try {
		File file = new File(targetImg);
		Image image = ImageIO.read(file);
		int width = image.getWidth(null);
		int height = image.getHeight(null);
		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = bufferedImage.createGraphics();
		g.drawImage(image, 0, 0, width, height, null);
		Image waterImage = ImageIO.read(new File(waterImg)); // 水印文件
		int width_1 = waterImage.getWidth(null);
		int height_1 = waterImage.getHeight(null);
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
		int widthDiff = width - width_1;
		int heightDiff = height - height_1;
		if (x < 0) {
			x = widthDiff / 2;
		} else if (x > widthDiff) {
			x = widthDiff;
		}
		if (y < 0) {
			y = heightDiff / 2;
		} else if (y > heightDiff) {
			y = heightDiff;
		}
		g.drawImage(waterImage, x, y, width_1, height_1, null); // 水印文件结束
		g.dispose();
		ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, file);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

2、添加文字水印

/**
* 添加文字水印
*
* @param targetImg
* 目标图片路径
* @param pressText
* 水印文字
* @param fontName
* 字体名称, 如:宋体
* @param fontStyle
* 字体样式,如:粗体和斜体(Font.BOLD|Font.ITALIC)
* @param fontSize
* 字体大小,单位为像素
* @param color
* 字体颜色
* @param x
* 水印文字距离目标图片左侧的偏移量,如果x<0, 则在正中间
* @param y
* 水印文字距离目标图片上侧的偏移量,如果y<0, 则在正中间
* @param alpha
* 透明度(0.0 – 1.0, 0.0为完全透明,1.0为完全不透明)
*/

public static void addTextWeatermark(String targetImg, List<ImageDrawTextConfig> textConfig, float alpha, boolean createImg, String newFileUrl) {
	try {
		File file = new File(targetImg);
		Image image = ImageIO.read(file);
		int width = image.getWidth(null);
		int height = image.getHeight(null);
		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g = bufferedImage.createGraphics();
		g.drawImage(image, 0, 0, width, height, null);
		//添加文字水印
		drawString(g, width, height, textConfig);
		g.dispose();
		if(createImg){
			File newFile = new File(newFileUrl);
			FileUtils.forceMkdir(newFile.getParentFile());
			ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, newFile);
		} else {
			ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, file);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

/**
 * 添加文字水印
 * @param g
 * @param imageWidth 图片宽度
 * @param imageHeight 图片高度
 * @param pressText 添加的文字水印
 * @param fontName 字体名称
 * @param fontStyle 字体样式
 * @param fontSize 字体大小的磅值
 * @param color 颜色
 * @param x 偏离宽
 * @param y 偏离高
 * @param widthScale 字体大小误差比例
 */
public static void drawString(Graphics2D g, int imageWidth, int imageHeight, List<ImageDrawTextConfig> textConfig){
	//font_name,字体的名称。如:“SansSerif”、“Times”、“黑体”、“宋体”等。
	//font_style,字体的样式。如:Font.PLAIN(正常体)、Font.BOLD(加粗体)、Font.ITALIC(斜体)等。
	//font_size,字体大小的磅值,是一个数字。如:10、12、14、18、20等。
	for(ImageDrawTextConfig config: textConfig){
		g.setFont(new Font(config.getFontName(), config.getFontStyle(), config.getFontSize()));
		g.setColor(config.getColor());
		int width_1 = config.getFontSize() * getLength(config.getPressText());
		//非正常字体的相应的增加整体的宽度,才不会类似靠最右边会丢失文字
		if(config.getFontStyle() != Font.PLAIN){
			width_1 += getLength(config.getPressText()) * config.getWidthScale();
		}
		int height_1 = config.getFontSize();
		int widthDiff = imageWidth - width_1;//剩余宽度
		int heightDiff = imageHeight - height_1;//剩余高度
		if (config.getX() < 0) {
			config.setX(widthDiff);
		} else if (config.getX() > widthDiff) {
			config.setX(widthDiff);
		}
		if (config.getY() < 0) {
			config.setY(heightDiff);
		} else if (config.getY() > heightDiff) {
			config.setY(heightDiff);
		}
		g.drawString(config.getPressText(), config.getX(), config.getY() + height_1);
	}
}

import java.awt.Color;

/**

  • 图片添加文字水印配置
  • @author smart_cwx

*/
public class ImageDrawTextConfig {

/** 添加的水印文字 */
String pressText;
/** 字体的名称 */
String fontName;
/** 文字样式 */
int fontStyle;
/** 字体大小的磅值 */
int fontSize;
/** 颜色 */
Color color;
/** 偏离x宽度 */
int x;
/** 偏离y高度 */
int y;
/** 字体大小误差比例 */
float widthScale;



public ImageDrawTextConfig(String pressText, String fontName, int fontStyle, int fontSize, Color color, int x,
		int y, float widthScale) {
	super();
	this.pressText = pressText;
	this.fontName = fontName;
	this.fontStyle = fontStyle;
	this.fontSize = fontSize;
	this.color = color;
	this.x = x;
	this.y = y;
	this.widthScale = widthScale;
}

public String getPressText() {
	return pressText;
}
public void setPressText(String pressText) {
	this.pressText = pressText;
}
public String getFontName() {
	return fontName;
}
public void setFontName(String fontName) {
	this.fontName = fontName;
}
public int getFontStyle() {
	return fontStyle;
}
public void setFontStyle(int fontStyle) {
	this.fontStyle = fontStyle;
}
public int getFontSize() {
	return fontSize;
}
public void setFontSize(int fontSize) {
	this.fontSize = fontSize;
}
public Color getColor() {
	return color;
}
public void setColor(Color color) {
	this.color = color;
}
public int getX() {
	return x;
}
public void setX(int x) {
	this.x = x;
}
public int getY() {
	return y;
}
public void setY(int y) {
	this.y = y;
}
public float getWidthScale() {
	return widthScale;
}
public void setWidthScale(float widthScale) {
	this.widthScale = widthScale;
}

}

/**
 * 获取字符长度,一个汉字作为 1 个字符, 一个英文字母作为 0.5 个字符
 * 
 * @param text
 * @return 字符长度,如:text="中国",返回 2;text="test",返回 2;text="中国ABC",返回 4.
 */
public static int getLength(String text) {
	int textLength = text.length();
	int length = textLength;
	for (int i = 0; i < textLength; i++) {
		if (String.valueOf(text.charAt(i)).getBytes().length > 1) {
			length++;
		}
	}
	return (length % 2 == 0) ? length / 2 : length / 2 + 1;
}

3、图片缩放-高清

以下包jar的引用,直接复制到类中:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

/**
 * 图片缩放
 * 
 * @param sourceFile 需要缩放的图片
 * @param savePath 缩放后的图片保存路径
 * @param height 高度
 * @param width 宽度
 * @param bb 比例不对时是否需要补白
 */
@SuppressWarnings("restriction")
public static double resize(String sourceFile, String savePath, int height, int width, boolean bb) {
	double scale = 1;
	try {
		Image image = ImageIO.read(new File(sourceFile));
		// 计算比例
		if ((image.getHeight(null) > height) || (image.getWidth(null) > width)) {
			if (image.getHeight(null) > image.getWidth(null)) {
				scale = (new Integer(height)).doubleValue() / image.getHeight(null);
			} else {
				scale = (new Integer(width)).doubleValue() / image.getWidth(null);
			}
		}
		//设置新画布的大小
		int w = (int)(image.getWidth(null)*scale);
		int h = (int)(image.getHeight(null)*scale); 
		Image _img = image.getScaledInstance(w, h, Image.SCALE_SMOOTH);
		BufferedImage tmp = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = tmp.createGraphics();
        //开启文字抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        //画图
        g2.drawImage(_img, 0, 0, null);
        g2.dispose();
        //生成输出的目标文件
        File newFile = new File(savePath);
		FileUtils.forceMkdir(newFile.getParentFile());
		/*输出到文件流*/
        FileOutputStream newImage = new FileOutputStream(savePath);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newImage);
        JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tmp);
        /* 压缩质量 */
        jep.setQuality(1f, true);
        encoder.encode(tmp, jep);
        /*近JPEG编码*/
        newImage.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
	return scale;
}

4、合并图片

/**
 * 将集合地址转为字符串
 * 
 * @param list 地址集合
 * @return
 */
public static String listToString(List<String> list){
	StringBuilder sb = new StringBuilder();
	for(String s: list){
		sb.append(s);
		sb.append(",");
	}
	return list.isEmpty()? "":sb.toString().substring(0, sb.toString().length()-1);
}

/** 
 * load image 
 * */  
public static BufferedImage loadImage(String filePath) {  
    try {  
        return ImageIO.read(new File(filePath));  
    } catch (Exception e) {  
        throw new RuntimeException(e);  
    }  
} 

/** 
 * convert to byte array. 
 * */  
public static byte[] convert2bytes(BufferedImage image, String format) {  
    try {  
        ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);  
        ImageIO.write(image, format, bout);  
        byte[] data = bout.toByteArray();  
        return data;  
    } catch (Exception e) {  
        throw new RuntimeException(e);  
    }  
}  

/**
 * 多张图片合并
 * 
 * @param fileFolders 文件存储基础路径,eg: /usr/
 * @param nameTypePath 文件储存基础路径下的分支路径 eg: img
 * @param imageUrls 要合并的图片基础路径下的地址 eg: /xxxxxxxxxxxx/xxxx.png
 * @return
 */
public static String mergeManyImage(String fileFolders, String nameTypePath, List<String> imageUrls, int maxWidth, int maxHeight) {
	List<String> newImageUrls = new ArrayList<String>();
	try {
		double scale = 1;
		// 定义暂时存储读取的文件集合
		List<File> targetFiels = new ArrayList<File>();
		//读取文件
		readFiles(fileFolders, imageUrls, targetFiels);
		List<Image> images = new ArrayList<Image>();
		readImages(targetFiels, images);
		//日期层级文件夹名称路径
		String dateFolder = "/" + CommonUtils.ymdhmsdfStr.format(new Date()) + (int) (1 + Math.random() * (100 - 1 + 1)) + "/";
		String name;
		if(images.size() > 0){
			int height = 0, index = 0;
			double currentHeight = 0;
			boolean isResize = false;
			List<String> imageTemps = new ArrayList<>();
			for(Image image: images){
				currentHeight = image.getHeight(null);
				height += currentHeight;
				//如果图片的宽度超过纸的大小,需要进行缩放
				if(image.getWidth(null) > maxWidth){
					height -= currentHeight;
					name =  UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
					//记录缩放图片地址
					List<String> resizeFile = new ArrayList<String>();
					resizeFile.add(fileFolders + imageUrls.get(index));
					//缩放图片
					scale = resize(fileFolders + imageUrls.get(index), fileFolders + nameTypePath + dateFolder + name, image.getHeight(null), maxWidth, false);
					//读取缩放后的图片高度
					currentHeight = currentHeight * scale;
					height += currentHeight;
					//用新生成的图片进去合并
					imageTemps.add(fileFolders + nameTypePath + dateFolder + name);
					isResize = true;
				}
				//超过纸则需要另起一张图片
				if(height > maxHeight){
					//创建本次图片
					name =  UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
					combinePicture(maxWidth, maxHeight, imageTemps, fileFolders + nameTypePath + dateFolder + name);
					newImageUrls.add("/" + nameTypePath + dateFolder + name);
					//重置新一张图片
					imageTemps.clear();
					height = (int) currentHeight;
				}
				if(!isResize){
					imageTemps.add(fileFolders+imageUrls.get(index));
				}
				index++;
				isResize = false;
			}
			//判断最后一轮是否有刚好剩下的图片,另起一张
			if(imageTemps.size() > 0){
				name =  UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
				combinePicture(maxWidth, maxHeight, imageTemps, fileFolders + nameTypePath + dateFolder + name);
				newImageUrls.add("/" + nameTypePath + dateFolder + name);
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	return newImageUrls.size() > 0?listToString(newImageUrls):null;
}

/**
 * 合并图片
 * 
 * @param width 新生成的图片的宽度
 * @param height 新生成的图片的高度
 * @param images 需要合并的图片对象集合
 * @param targetUrl 生成的图片路径
 */
public static void combinePicture(int width, int height, List<String> files, String targetUrl){
	//构造一个类型为预定义图像类型之一的 BufferedImage。 宽度为第一只的宽度,高度为各个图片高度之和
	BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
	//绘制合成图像
	Graphics2D g = tag.createGraphics();
	//开启文字抗锯齿
    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
	g.fillRect(0, 0, width, height);
	g.setColor(Color.WHITE);
	g.drawLine(0, 0, width, height);
	int tempHeight = 0;
	try {
		for(String image: files){
			BufferedImage sourceImage = loadImage(image);
			byte[] pngData = convert2bytes(sourceImage, PICTRUE_FORMATE_PNG);  
			ByteArrayInputStream bais = new ByteArrayInputStream(pngData);
			BufferedImage bi = ImageIO.read(bais);
			g.drawImage(bi, 0, tempHeight, null); 
			tempHeight += sourceImage.getHeight(null);
			bais.close();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
	// 释放此图形的上下文以及它使用的所有系统资源。
	g.dispose();
	try {
		File custodyFile = new File(targetUrl);
		FileUtils.forceMkdir(custodyFile.getParentFile());
		//输出图片
		ImageIO.write(tag, PICTRUE_FORMATE_PNG, custodyFile); 
	}catch(IOException e){
		
	}
}

/**
 * 读取图片文件附件
 * 
 * @param targetFiels 图片文件对象
 * @param images 暂时存储的图片对象
 */
public static void readImages(List<File> targetFiels, List<Image> images){
	try {
		for(File f: targetFiels){
			images.add(javax.imageio.ImageIO.read(f));
		}
	} catch (IOException e) {
		System.out.println("读取图片文件出错:");
		e.printStackTrace();
	}
}

/**
 * 读取需要合并的图片
 * 
 * @param fileFolders 文件路径前缀
 * @param imageUrls 图片地址后缀
 * @param targetFiels 存储读取出来的文件集合
 */
public static void readFiles(String fileFolders, List<String> imageUrls, List<File> targetFiels){
	for(String url: imageUrls){
		File _file = new File(fileFolders + url);
		if(_file.exists()){//判断文件是否存在
			targetFiels.add(_file);
		}
	}
}

完成!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值