Flutter 像素编辑器#04 | 导入导出图像


theme: cyanosis

1. 本文目的

本系列,将通过 Flutter 实现一个全平台的像素编辑器应用。源码见开源项目 【pix_editor】。在前三篇中,我们已经完成了一个简易的图像编辑器,并且简单引入了图层的概念,支持切换图层显示不同的像素画面。

本文的目标两个:

[1]. 支持导入一张图像,将其像素化地展示在界面中:

01.gif

[2]. 像素点可编辑,编辑完成后,可以将图片进行导出到对应文件夹:

02.gif

image.png


2. 图像的导入

图像本质上是由一个个像素点构成的二维空间点阵。在像素编辑器中,每个单元格记录着一份像素信息,我们需要根据网格行列数,对图像的像素信息进行采样。行列数会直接决定当前区域中像素信息相对于原图像的的完整程度。比如下面分别是 16*1632*3264*64 的网格采样同一图像的呈现效果:

| 16*16 | 32*32 | 64*64 | | --- | --- |--- | | image.png | image.png |image.png

当前需求的关键点在于:如何获取原图像的所有像素点信息,然后根据像素点映射生成 PixCell 列表数据进行呈现。此时需要依赖 image类库。 现在在业务逻辑对象 PixPaintLogic 中增加一个 setImage 方法,传入用户选择的文件路径,使用 img.decodeImage 方法解码图片生成 img.Image 对象:

```dart ---->[lib/bloc/pixpaintlogic.dart]---- import 'package:image/image.dart' as img;

void setImage(String filePath) async { File file = File(filePath); img.Image? pixImage = img.decodeImage(await file.readAsBytes()); if(pixImage==null){ return; } setPixByImage(pixImage); } ```

setPixByImage 方法中处理核心逻辑:遍历网格的行列数,从 image 中采样对应的像素值。其中 rate 标识格点像素相较于真实像素的坐标缩放比例,也就是像素采样的间隔。将非透明色的像素点,加入到 PixCell 列表中:

```dart void setPixByImage(img.Image image) { List cells = []; int minSize = min(image.width, image.height); int minCount = min(row, column); int count = minSize.clamp(0, minCount); double rate = minSize / count;

for (int x = 0; x < count; x++) { for (int y = 0; y < count; y++) { var pixel = image.getPixel((y * rate).toInt(), (x * rate).toInt()); var color = Color.fromARGB( pixel.a.toInt(), pixel.r.toInt(), pixel.g.toInt(), pixel.b.toInt()); if (color != Colors.transparent) { cells.add(PixCell(color: color, position: (y, x))); } } } setPixCells(cells); } ```

最后通过 setPixCells 方法,将生成的 PixCell 列表加入到状态数据中,并发送更新通知,让画板进行变化:

dart void setPixCells(List<PixCell> cells) { pixCells.clear(); pixCells.addAll(cells); notifyListeners(); activePixLayer.update(); }


3. 图像的导出

本来是想通过 Canvas 进行绘制导出图片的,但是效果并不理想,因为 Flutter 的 1px 问题,并不适合绘制细小的像素。 image类库 中提供了像素级的操作,直接生成 png 图像:

image.png

如下所示,先创建一个 pixLayer 网格宽高的 img.Image 图像,通过数为 4 个,默认是 3 没有透明度。然后遍历 pixLayer#pixCells 为生成的 image 对象设置像素信息。最后只需要 img.encodePng(image) 即可进行编码得到字节列表数据:

```dart PixPaintLogic paintLogic = PixPaintScope.read(context); PixLayer pixLayer = paintLogic.activePixLayer;

final image = img.Image( width: pixLayer.row, height: pixLayer.column, numChannels: 4, );

for (PixCell pix in pixLayer.pixCells) { Color color = pix.color; image.setPixelRgba( pix.position.$1, pix.position.$2, color.red, color.green, color.blue, color.alpha, ); }

final Uint8List byteData = img.encodePng(image); ```


有了字节列表数据之后,通过 FilePicker.platform.saveFile 选择保存文件,然后写入文件即可:

dart String? result = await FilePicker.platform.saveFile(type: FileType.image); if (result != null) { File file = File(result); await file.writeAsBytes(byteData); }


到这里,导入导出图像的功能就基本完成了,这样像素编辑的基本功能就能运转了,但目前只支持正方形的图片,后续会继续优化,支持矩形的网格。可以多多关注 【pix_editor】 的发展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值