前言
前些时候,我在某个网站上注册了一个账号,在修改个人头像时,发现该网站仅支持矩形头像的显示,但是我个人是比较喜欢圆形头像的; 因此我需要将电脑上一张矩形图片处理成白底圆形的图片,但是我找了好多在线方法(ps:不太喜欢给电脑上下载许多不常用的工具),发现
构思
在Java语言中,其实有一个操作图像的类库,就是 BufferedImage
。
Image 是一个抽象类,BufferedImage 是 Image 的实现类,是一个操作缓冲区图像的类。BufferedImage 的主要作用是将一张图片加载到内存中,
然后我们就可以实现对图像的一些操作。比如:获得绘图对象、图像缩放、选择图像平滑度、图片大小变换、改变图片显示颜色、设置透明度等功能。
实现
package com.abc.learn;
import org.apache.commons.io.FilenameUtils;
import org.junit.Test;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
/**
* @author 030
* @date 13:05 2021/9/24
* @description
*/
public class ToCirclePicture {
// 本地图片示例
private static final String localImgPath = "C:/Users/Administrator/Pictures/4K/312591.jpg";
// 网络图片以我图床上的一张图为例
private static final String netImgUrl = "https://pic.imgdb.cn/item/613e1f7c44eaada739eb4a11.jpg";
/**
* 传入的图像必须是正方形的 才会 变成圆形
* 如果是长方形的比例则会变成椭圆的
*/
public static BufferedImage changeToCircular(BufferedImage bufferedImage, int radius) {
// 这种是黑色底的
// BufferedImage destImg = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
// 透明底的图片,长、宽都为 既定的半径
BufferedImage destImg = new BufferedImage(radius, radius, BufferedImage.TYPE_4BYTE_ABGR);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, radius, radius);
Graphics2D g2 = destImg.createGraphics();
g2.setClip(shape);
// 使用 setRenderingHint 设置抗锯齿
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(bufferedImage, 0, 0, null);
// 设置颜色
g2.setBackground(Color.white);
g2.dispose();
return destImg;
}
/**
* 缩小Image,此方法返回源图像按给定宽度、高度限制下缩放后的图像
*
* @param sourceImage
* @param newWidth :压缩后宽度
* @param newHeight :压缩后高度
*/
public static BufferedImage scaleByPercentage(BufferedImage sourceImage, int newWidth, int newHeight) {
// 获取原始图像透明度类型
int type = sourceImage.getColorModel().getTransparency();
int width = sourceImage.getWidth();
int height = sourceImage.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.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.drawImage(sourceImage, 0, 0, newWidth, newHeight, 0, 0, width, height, null);
graphics2d.dispose();
return img;
}
/**
* 获取字符串的base64编码
*/
public static String getBase64(String str) {
String destStr = "";
try {
byte[] sourceArray = str.getBytes(StandardCharsets.UTF_8);
// 对 byte[] 进行base64编码
destStr = new BASE64Encoder().encode(sourceArray);
} catch (Exception e) {
e.printStackTrace();
}
return destStr;
}
/**
* 测试 base64 编码
*/
public void testBase64() {
String oldStr = "hello java";
String newStr = getBase64(oldStr);
System.out.println(newStr); // aGVsbG8gamF2YQ==
}
/**
* 测试 传入的是一个本地硬盘图片时,裁剪尺寸成矩形
*/
public void testLocalImg() {
try {
// BufferedImage是一个Image的实现类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区可以方便地操作这个图片)
// java 中将一副本地图片加载到内存的方式
// ImageIO 提供read()和write()静态方法,读写图片,比以往的InputStream读写更方便
BufferedImage sourceImg = ImageIO.read(new FileInputStream(localImgPath)); // 当然你这里使用File实例构造也是可以的
// 拿到 BufferedImage 实例之后,我们就可以对图片进行各种操作了,比如获得绘图对象、图像缩放、选择图像平滑度等功能,通常用来做图片大小变换、图片变灰、设置透明不透明等
// 将原始图片,处理成既定大小的矩形图片
BufferedImage destImg = scaleByPercentage(sourceImg, sourceImg.getWidth(), sourceImg.getHeight());
File outputFile = new File(FilenameUtils.getBaseName(localImgPath) + "-副本-" + Instant.now().toEpochMilli() + ".png");
// 传输中,图片是不能直接传的,需要先转为字节数组再传输较为方便;而字节数组再转回BufferedImage则还原图片 BufferedImage–>byte[]
ImageIO.write(destImg, "png", outputFile);
System.out.println("转换图片完成");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 测试 当传入的是网络图片地址时,裁剪图像成矩形
*/
public void testNetUrlImg() {
try {
// BufferedImage是一个Image的实现类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区可以方便地操作这个图片)
// java 中将一个网络图片加载到内存的方式
// ImageIO 提供read()和write()静态方法,读写图片,比以往的InputStream读写更方便
BufferedImage sourceImg = ImageIO.read(new URL(netImgUrl));
// 拿到 BufferedImage 实例之后,我们就可以对图片进行各种操作了,比如获得绘图对象、图像缩放、选择图像平滑度等功能,通常用来做图片大小变换、图片变灰、设置透明不透明等
// 将原始图片,处理成既定大小的矩形图片
BufferedImage destImg = scaleByPercentage(sourceImg, sourceImg.getWidth(), sourceImg.getHeight());
// 获取网络图片的名字
String name = FilenameUtils.getBaseName(netImgUrl);
File outputFile = new File(name + "-副本-" + Instant.now().toEpochMilli() + ".png");
// 传输中,图片是不能直接传的,需要先转为字节数组再传输较为方便;而字节数组再转回BufferedImage则还原图片 BufferedImage–>byte[]
ImageIO.write(destImg, "png", outputFile);
System.out.println("转换图片完成");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将本地图片裁剪成圆形
*/
public void testLocalImgToCircular() {
try {
// BufferedImage是一个Image的实现类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区可以方便地操作这个图片)
// java 中将一个网络图片加载到内存的方式
// ImageIO 提供read()和write()静态方法,读写图片,比以往的InputStream读写更方便
BufferedImage sourceImg = ImageIO.read(new File(localImgPath));
// 拿到 BufferedImage 实例之后,我们就可以对图片进行各种操作了,比如获得绘图对象、图像缩放、选择图像平滑度等功能,通常用来做图片大小变换、图片变灰、设置透明不透明等
// 将原始图片,处理成 圆形,注意:需要按照矩形的短边裁剪
BufferedImage destImg = changeToCircular(sourceImg, sourceImg.getHeight()); // 圆形的半径,就按照原始图片的高度来设置
// 将 BufferedImage 缓冲区图像,输出保存
/**
* 注意:这里有一个很无语的点就是:不管原图片是什么格式的,你最后输出保存的文件格式必须是png的,如果你使用jpg格式后,就会发现图片编程暗粉色的了,
* 这个bug我找了好久,不明白是什么原因,只能先这样了。
*/
File outputFile = new File(FilenameUtils.getBaseName(localImgPath) + "-副本-圆形" + Instant.now().toEpochMilli() + ".png");
ImageIO.write(destImg, "png", outputFile);
System.out.println("图片已裁剪成圆形");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 测试 commons-io 中的 FilenameUtils 对文件的操作
*/
public void testUtils() {
// 如果 字符串 是 路径字符串时,才可用如下方式
// 获取文件名字,包含后缀
String str = FilenameUtils.getName(localImgPath);
// 获取文件名字,不包含后缀
String baseName = FilenameUtils.getBaseName(localImgPath);
System.out.println(str);
System.out.println(baseName);
}
/**
* 测试 jdk1.8 中 关于时间的工具类
*/
public void testNewTime() {
Instant instant = Instant.now();
// 获取秒 1632641950
System.out.println(instant.getEpochSecond());
// 获取毫秒 1632641950389
System.out.println(instant.toEpochMilli());
// 获取纳秒 389000000
System.out.println(instant.getNano());
}
}
特别注意上面的裁剪成圆形图像的方法,最后输出的文件格式要是png的才可以,至于原因我暂时不清楚。 文章参考:https://blog.csdn.net/jiachunchun/article/details/89670721
0 Comments