likes
comments
collection
share

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

作者站长头像
站长
· 阅读数 17

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

Flutter&Rust#01系列文章:


前两篇介绍了如何 Rust 在Flutter 中的基本使用方式。接下来,我们将基于 Rust 实现一个非常实用的功能:图片格式转换器。该功能将作为 匠心千刃 的一部分。

有时想要进行图片格式转化比较麻烦,绝大多数转换器都需要登录、收费,更别说批量转换这种高级需求了,实用的移动端转化软件更是凤毛麟角。 匠心千刃作为一个 Flutter 构建的全平台的工具应用,在未来发布之后,图片格式转换的功能将供所有人 免费使用,毕竟这玩意没有任何的服务成本。

由于图片的格式非常多,不同格式的图片特点,会分篇进行介绍。本文主要介绍输出是 jpg/jpegwebp 两种格式的转换。


1. 了解 Jpeg 和 webp 格式的图片

我们知道,图片是由二维像素点构成的色彩空间。每个像素点承载的色彩数据将通过二进制进行编码,持久化到文件中。色彩数据本身比较大,一般图片格式会在编码的过程中进行压缩存储,从而便于存储和传输

有损压缩和无损压缩

  • 无损压缩会保留 全部的色彩信息,图片体积就会相对较大。如 png、bmp;
  • 有损压缩 丢失一部分色彩信息,在可接受的程度下,减少图片体积大小。如 jpg/webp;

jpeg 和 jpg 格式

两者在本质上没有任何区别,全称是 Joint Photographic Experts Group。由于历史原因,早期的windows 操作系统支持三位后缀名,所以简写为 jpg .现代操作系统和软件完全支持 .jpg.jpeg 两种扩展名,二者可以互换使用,完全没有区别。

jpeg 的压缩算法是有损的,可以通过编码时的 质量 来控制损耗程度。 另外,jpeg 图片没有 Alpha 通道,也就是说 不支持透明度


webp 格式

webp 支持无损和有损压缩,在编码的过程中可以根据需要选择压缩模式。并且支持透明度(Alpha通道),与PNG 类似。文件体积较小,图像质量和压缩比之间平衡良好。

在 AndroidStudio 中有将 png 转化为 webp 的工具,如下搜索,在 75% 的质量下,可以将 3.4MB 的 png 图片转化为 113KB 大小的 webp 文件。仅占原图的 3% 。

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的


2. 转换应用交互

如下所示,在 多媒体处理/图片处理/格式转换 菜单中进行交互操作。面板中:

  • 头部是可编辑的表单,用于选择或输入转换的配置参数。比如输出格式、可损压缩的编码质量、输出目录等。
  • 中间是会展示选择和转换图片及其基本信息,通过右上角的关闭按钮可以移除选择。
  • 点击转换按钮处理转换任务,底部栏展示提示信息。

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

面板的界面布局整体呈上中下结构,通过 Column 组件竖直排列。其中格式下拉选择、文件选择输入、按钮等,使用 tolyui 的相关组件快速实现展示效果:

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

1.45 MB 的 png 图片通过 75% 的编码质量,可以将体积减少到原图的 10%。通过 rust 只需 70 ms 左右即可完成,效率还是非常可以的可以的。试了一下 Dart 通过 image 类库实现相同的转换,需要 360 ms 左右。


3. Rust 核心算法

Flutter 作为前端的交互界面,具体的转换功能交由 Rust 处理。如下所示,定义 convert_to_jpg 方法,传入输入和输出文件地址,以及编码时的质量数值。通过image 库的 JpegEncoder::new_with_quality 创建指定质量的 Jpeg 编码器即可:

use std::fs::File;
use std::io::BufWriter;
use image::codecs::jpeg::JpegEncoder;
use image::ImageReader;


pub fn convert_to_jpg(input_path: &str, output_path: &str, quality: u8)-> Result<(), image::ImageError>{
    let img = ImageReader::open(input_path)?.decode()?;
    let output_file = File::create(output_path)?;
    let writer = BufWriter::new(output_file);
    let mut encoder = JpegEncoder::new_with_quality(writer, quality);
    encoder.encode_image(&img)?;
    Ok(())
}

转换为 webp 时, lossless 可以指定是否启用无损压缩;quality 指定有损压缩的质量,也是 1~100。 当前版本的 image 好像不支持 webp 的有损压缩,这里额外通过 webp 库进行处理。

[dependencies]
image = "0.25.2"
webp = "0.3.0"
pub fn convert_to_webp(input_path: &str, output_path: &str, quality: f32, lossless: bool) -> Result<(), image::ImageError> {
    let img = ImageReader::open(input_path)?.decode()?;
    let encoder = Encoder::from_image(&img).expect("Failed to decode image");
    let webp_data: WebPMemory;
    if lossless {
        webp_data = encoder.encode_lossless();
    } else {
        webp_data = encoder.encode(quality);
    }
    fs::write(output_path, webp_data.as_bytes()).expect("Failed to write file");
    Ok(())
}

Rust 方面的逻辑处理完毕后,就可以通过工具生成桥接代码。

flutter_rust_bridge_codegen generate


4. webp 的压缩

webp 支持无损压缩,所以在头部的操作表单中加入一个 ChekedBox ,选中时表示需要无损压缩。此时编码质量的输入框将会消失。如下所示,75% 质量输出的 webp 只有 41.38 KB, 仅为原尺寸的 2%

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

对比 AndroidStudio 中的 webp 转换器可以看出,Rust 的算法效力是和它完全一致的。

Flutter&Rust#03 | 图片格式转换 jpeg/webp 前两篇介绍了如何 Rust 在Flutter 中的

对于一些需要打包入应用的图片文件,如果是过大的 png,可以考虑使用 webp 进行压缩,可以减少应用体积。网络图片也可以转换成 webp 提高加载的速度。现在掌握了 Rust 转换 webp 的技术手段,就可以打造更实用和好用的工具,比如支持批量图片转换。这将在 匠心千刃 的后续开发中逐步支持。


本文主要介绍了基于 rust 将图片转换为 jpeg/webp 格式的方式,后面将继续介绍其他图片格式的特点,以及优化匠心千刃的交互。随着项目的推进,将遇到很多 rust 和 dart 桥接的知识点,比如如何 dart 处理 rust 端的异常, rust 的代码如何异步处理,或返回 Stream 流。如何进行更复杂类型的参数传递等。敬请期待 ~

转载自:https://juejin.cn/post/7415672130190376960
评论
请登录