Java 图片缩放,转ico格式
Java 在转换图片格式为 ico 时 需要引入 image4j 的依赖,分享写好的源码和并通过exe4j 打包好的exe小程序。因为功能太少所以再引入 thumbnailator 的依赖缩放图片。
依赖
<!-- 引入依赖-->
<dependency>
<groupId>net.ifok.image</groupId>
<artifactId>image4j</artifactId>
<version>0.7.2</version>
</dependency>
<!-- 缩放图片的工具类 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.19</version>
</dependency>
ico图标转换
ico图标可以存储单个图案、多尺寸、多色板的图标文件。通常作为标志,目前主流的浏览器都支持ICO图标,在浏览器的地址栏、标签及收藏夹上显示,也可以用来,个性化设置文件夹或者程序。图标有一套标准的大小和属性格式,且通常是小尺寸的。(16,32,48,64,128,256)
截图来自:https://github.com/imcdonagh/image4j,主要就4个API,读取与转换 BMP与ICO两种格式。
这里只说 ICO。因为 ImageIO.write(缓冲图, “格式”, 流); 可以直接处理输出bmp格式。
@Test
public void test01() {
// 图片读取路径
String inputPath = "F://1.jpeg";
// 输出路径
String outputPath = "F://0.ico";
// 把需要关闭的流放在 try( 里 ){。。。
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputPath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputPath));
) {
// 读取 图片
BufferedImage img = ImageIO.read(bis);
// 写出 图片
// ICOEncoder.write(Arrays.asList(img), bos);
ICOEncoder.write(Collections.singletonList(img), bos);
} catch (IOException e) {
e.printStackTrace();
}
}
// 其他格式jpg/jpeg/png/gif/bmp 写出图片 记得格式后缀与格式相同 jpg比较省空间
ImageIO.write(img,"jpg", bos);
因为一般ico图标尺寸都在256以下,这个工具类最多也就转换300*300 的图标,所以需要进行图片的缩放操作。
Thumbnails缩放操作
主要的一些操作。
.of ( ) 文件路径 ,流 或者 缓存图片
.toFile( “D://xxx.jpg” ) 文件转存路径
.asBufferedImage() 返回缓存图片
.toOutputStream(os) 赋予输出流
.size( w, h ) 按指定大小调整,配值 .keepAspectRatio(false) 取消默认的比例变化,会变形
.scale(0.25f) 按比例缩放。
.sourceRegion(x, y, w, h) 裁剪,前两个参数可以用枚举类 Positions.
这个在另一篇里用到了
.rotate(-90) 旋转 正数:顺时针 负数:逆时针
.watermark(位置,水印图,透明度) 加水印
.outputQuality(0.8f) 图片质量
.outputFormat(“png”) 转图片格式 ,鸡肋
这里简单使用一下
@Test
public void test02() {
// 图片读取路径
String inputPath = "F://1.jpeg";
// 输出路径
String outputPath = "F://1.ico";
// 把需要关闭的流放在 try( 里 ){。。。
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputPath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputPath));
) {
// 读取 图片
BufferedImage img = ImageIO.read(bis);
//of( inputPath / img ) 都可以
img= Thumbnails.of(img).size(256, 256).keepAspectRatio(false).outputQuality(1f).asBufferedImage();
// 写出 图片
ICOEncoder.write(Collections.singletonList(img), bos);
} catch (IOException e) {
e.printStackTrace();
}
}
好了主要功能就这些,为了方便以后使用,把这些东西写成窗体应用,导入使用一个美化包 flatlaf。
<!-- GUI窗体应用美化包 -->
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId>
<version>3.1</version>
</dependency>
使用:
// 初始化美化包。
FlatLightLaf.installLafInfo();
try {
// 设置美化包风格。
// FlatDarkLaf()、FlatLightLaf()、
// FlatDarculaLaf()、FlatIntelliJLaf()这四种对象风格
UIManager.setLookAndFeel(new FlatLightLaf());
} catch (Exception ex) {
System.err.println("窗体美化包加载失败!!");
}
窗体页面样式:
展开后:
//转存路径默认是桌面地址。
String homePath=FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();
选择一张图片:
这个浏览文件是一个现成的窗体 JFileChooser 具体使用:
@Test
public void test03() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("文件资源选择");
// 设置默认选择路径 new File(".")//当前文件的目录,,
chooser.setCurrentDirectory(new File("D://"));
// 设置可选择文件
// FILES_ONLY: 只能选文件;DIRECTORIES_ONLY: 只能选文件夹;
// FILES_AND_DIRECTORIES: 文件和文件夹都可以选。
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setMultiSelectionEnabled(true);//是否可多选,默认false
// 设置文件过滤器(可选择的文件类型):默认所有文件
// chooser.setFileFilter("提示信息","后缀1","2");//设置默认使用的文件过滤器
// chooser.addChoosableFileFilter(..);//设置候选的文件过滤器
// eg:
chooser.addChoosableFileFilter(
new FileNameExtensionFilter("zip(*.rar,...)", "zip", "rar", "7z"));//压缩文件过滤器
chooser.setApproveButtonText("这是选择按钮");// 设置按钮文本
// CANCEL_OPTION: 点击了取消或关闭 ;APPROVE_OPTION: 点击了确认或保存 ;
// ERROR_OPTION: 出现错误。
int state=chooser.showOpenDialog(null);
if(state== JFileChooser.APPROVE_OPTION){
// 点击了 这是选择按钮
// 获取选择的文件流,前面若是设置了多选这里记得用getSelectedFile接取
File file = chooser.getSelectedFile();
System.out.println(file.getAbsoluteFile().toString());
}
}
这里简单使用一下:
/** 筛选图片*/
private void scanPath() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("筛选图片");
chooser.setCurrentDirectory(new File("D://"));
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setMultiSelectionEnabled(false);
// 设置默认筛选图片
chooser.setFileFilter(
new FileNameExtensionFilter("image(*.jpg,*.png,.gif)", "jpeg", "jpg", "png", "gif"));
chooser.setApproveButtonText("选择");
int state = chooser.showOpenDialog(null);
if (state == JFileChooser.APPROVE_OPTION) {
// 获取选择的文件流
File pathFiles = chooser.getSelectedFile();
if (!pathFiles.isDirectory()) {
// 当选择的是文件的时候触发记录绝对路径,
// 没考虑闲的无聊改筛选设置为全部,然后选不是图片的文件。
inputPath = pathFiles.getAbsolutePath();
。。。
}
}
}
这个文件选择的弹框是现成的,也可以自己写一些功能弹框。
注意事项:
// 子窗体的关闭的配置 不要使用 EXIT_ON_CLOSE
child.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
// 子窗体关闭除了点×还可以在按钮事件里调用 System.exit(0); 关闭
这是选择图片后的预览:
因为是直接在面板绘制的没考虑画面重叠的问题这里简单解决一些:
// 获取面板画笔对象
final Graphics g = imageBox.getGraphics();
// 这里设置画笔颜色为面板颜色,然后调用填充方法直接把整个面板初始化为背景色
g.setColor(imageBox.getBackground());
g.fillRect(0,0,imageBox.getWidth(),imageBox.getHeight());
BufferedImage img2 = null;
try {
img2 = Thumbnails.of(image).size(imageBox.getWidth(),imageBox.getHeight())
.asBufferedImage();
} catch (IOException e) {
e.printStackTrace();
}
assert img2 != null;
g.drawImage(img2, 0, 0, null);
双缓冲
这样虽然可以解决,但是会绘制两次,有点闪,所有可以先用画笔在图片上绘制,处理好之后在统一绘制到面板上,这样也可以避免一些高帧渲染问题。
// 获取面板画笔对象
final Graphics g = imageBox.getGraphics();
// // 这里设置画笔颜色为面板颜色,然后调用填充方法直接把整个面板初始化为背景色
// g.setColor(imageBox.getBackground());
// g.fillRect(0,0,imageBox.getWidth(),imageBox.getHeight());
BufferedImage img2 = null;
try {
img2 = Thumbnails.of(image).size(imageBox.getWidth(), imageBox.getHeight()).asBufferedImage();
} catch (IOException e) {
e.printStackTrace();
}
assert img2 != null;
// g.drawImage(img2, 0, 0, null);
// 新建图层,获取画笔对象,
BufferedImage img3 = new BufferedImage(imageBox.getWidth(), imageBox.getHeight(),BufferedImage.TYPE_INT_RGB);
Graphics g2d=img3.createGraphics();
g2d.setColor(imageBox.getBackground());
// img3图片底色填充,画笔也可以写文字
g2d.fillRect(0,0,img3.getWidth(),img3.getHeight());
g2d.drawImage(img2, 0, 0, null);
g.drawImage(img3, 0, 0, null);
这样处理后只用绘制一次,而且图片已经在缓存里绘制起来更快。
透明度
// 图层也可以是透明色
BufferedImage buffImg = new BufferedImage(width, heigth, BufferedImage.TYPE_INT_RGB);
/**获取画笔对象*/
Graphics2D g2d = buffImg.createGraphics();
// ---------- 增加下面的代码使得背景透明 -----------------
buffImg = g2d.getDeviceConfiguration().createCompatibleImage(width, heigth, Transparency.TRANSLUCENT);
// 注,此时图片画笔对象需要重新获取一下
// g2d.dispose();//释放画笔,可省
g2d = buffImg.createGraphics();
// 透明图层能用来干嘛我也不清楚,不过输出带透明度的图片格式记得设定png,因为jpg省空间但有损。
//文字水印,及简单图像
g2d.setColor(color);
// 设置 Font
g2d.setFont(new Font("微软雅黑", Font.ROMAN_BASELINE, 33));//默认:Dialog
//设置透明度:1.0f为透明度 ,值从0-1.0,依次变得不透明
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.drawString("水印", x, y);// 文字
g2d.drawRect(x,y,width,heigth);// 绘制矩形
g2d.fillPolygon(int[] x坐标,int[] y坐标,int 点数量);// 点连线闭合图形
这里的缩放也是使用 Thumbnails 的工具类方法,肯定还有其他同类型的工具类,如果只是应用程序进行鼠标滚轮缩放图片仅显示的话,缩放画布就可以了。
// 缩放画布
g.scale(scale, scale);
// 这个缩放的画笔对象是面板,原图直接绘制,
double scale = 面板宽*1.0/原图宽;
// 如果用 双缓冲 那新建图层的长宽就是,面板长,宽/scale
// 这种缩放简单,只能用于显示,没有处理后的结果不能保存;
这里的缩放也是使用 Thumbnails 的工具类方法,肯定还有其他同类型的工具类,如果只是应用程序进行鼠标滚轮缩放图片仅显示的话,缩放画布就可以了。
// 缩放画布
g.scale(scale, scale);
// 这个缩放的画笔对象是面板,原图直接绘制,
double scale = 面板宽*1.0/原图宽;
// 如果用 双缓冲 那新建图层的长宽就是,面板长,宽/scale
// 这种缩放简单,只能用于显示,没有处理后的结果不能保存;
下载地址
已经打好exe包:https://pan.baidu.com/s/1cen_C6zZrauoJhWyYNuV1A?pwd=6666
源码:https://pan.baidu.com/s/17Xop5sXA2Bgf0xploa68vg?pwd=6666
jar包运行需要 jre 运行环境, 通过exe4j 可以一起打成 exe包直接运行。