Flutter缩放后重绘CustomPaint线条解决绘图放大像素化问题
问题背景
我正在使用Flutter开发一款笔记应用,绘图模块基于CustomPainter实现。为优化性能,我会在绘制若干线条后将CustomPaint内容转为图片存储,但缩放放大时,已转为图片的线条会出现严重像素化问题。我最初的解决方案是缩放后重绘线条,但如何正确重绘才能保证分辨率达标?此外是否可以仅重绘当前屏幕可视区域内的线条?
现有代码参考
布局约束与像素比获取
LayoutBuilder(builder: (context, constraints) { size = constraints.biggest; scale = MediaQuery.of(context).devicePixelRatio;
缩放交互实现
GestureDetector( onScaleStart: (details) { _initialFocalPoint = details.focalPoint; _initialScale = _scale; }, onScaleUpdate: (details) { setState(() { _sessionOffset = details.focalPoint - _initialFocalPoint; _scale = _initialScale * details.scale; }); }, onScaleEnd: (details) { setState(() { _offset += _sessionOffset; _sessionOffset = Offset.zero; }); }, child: Transform.translate( offset: _offset + _sessionOffset, child: Transform.scale( scale: _scale, child: ....
CustomPaint 组件代码
CustomPaint( size: size!, willChange: true, painter: DrawingViewPainter( pointsList: drawingNotifier.points, image: image ), );
绘制内容转缓存图片逻辑
ui.PictureRecorder recorder = ui.PictureRecorder(); Canvas canvas = ui.Canvas(recorder); canvas.scale(scale!); DrawingViewPainter(image: image, pointsList: points).paint(canvas, size!); ui.Picture picture = recorder.endRecording(); ui.Image newImage = await picture.toImage( (size!.width * scale!).ceil(), (size!.height * scale!).ceil(), ); // ... image = newImage
缓存图片绘制逻辑
canvas.drawImageRect( image!, Offset.zero & Size(image!.width.toDouble(), image!.height.toDouble()), Offset.zero & size, Paint() );
解决方案
1. 解决缩放像素化问题
当前你生成缓存图片时仅考虑了设备基础像素比,没有计入手势缩放的倍率,放大时低分辨率的缓存图被拉伸就会出现像素化。修改缓存图片生成逻辑,把手势缩放倍率也计入分辨率计算:
// 计算总缩放倍率 = 设备像素比 * 手势缩放倍率 final totalScale = scale! * _scale; ui.PictureRecorder recorder = ui.PictureRecorder(); Canvas canvas = ui.Canvas(recorder); canvas.scale(totalScale); DrawingViewPainter(image: image, pointsList: points).paint(canvas, size!); ui.Picture picture = recorder.endRecording(); ui.Image newImage = await picture.toImage( (size!.width * totalScale).ceil(), (size!.height * totalScale).ceil(), );
优化建议:手势缩放过程中临时直接绘制原始线条,不要用缓存图,缩放结束稳定后再重新生成对应倍率的缓存图,既保证操作流畅度,也不会出现缩放过程中的模糊问题。
2. 可视区域按需重绘实现
可以通过当前偏移量_offset和缩放倍率_scale计算出屏幕可视区域对应的画布坐标范围,遍历所有线条数据时,只把坐标落在这个范围内的线条传入DrawingViewPainter绘制,能大幅减少不必要的绘制开销。
3. 更优的性能方案
优先缓存ui.Picture矢量绘制指令而不是位图,矢量数据放大不会出现像素化,需要导出图片的时候再按需生成对应分辨率的位图即可,内存占用比反复生成高分辨率位图低很多。
内容的提问来源于stack exchange,提问作者Yusuf-Uluc
相关产品推荐
相关产品推荐

