Flutter如何缩放CustomPainter实现像素风格无抗锯齿显示效果?
Flutter CustomPainter 缩放保留像素风锐利效果方案
问题原因
直接对Canvas调用scale、或者用Transform.scale包裹组件时,Flutter默认使用双线性插值对绘制内容做平滑缩放,仅关闭Paint的抗锯齿无法影响缩放阶段的采样逻辑,因此会出现边缘模糊的问题,和像素风的显示要求不符。
可用解决方案
方案1:全参数缩放绘制(轻量场景首选)
不调用Canvas的scale方法,直接将所有绘制参数统一乘以scaleFactor,完全避开缩放插值流程:
class MyCustomPainter extends CustomPainter{ final double scaleFactor; final int originalWidth = 16; final int originalHeight = 16; MyCustomPainter(this.scaleFactor); @override void paint(Canvas canvas, Size size) { // 直接绘制缩放后的内容,不调用canvas.scale canvas.drawPaint(Paint()..color = Colors.white70); canvas.drawLine( Offset(4*scaleFactor,4*scaleFactor), Offset(12*scaleFactor,12*scaleFactor), Paint() ..color = Colors.black ..style = PaintingStyle.stroke ..strokeWidth = 1*scaleFactor // 线宽同步缩放 ..isAntiAlias = false // 关闭抗锯齿 ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
外层布局不需要调整,保持原有写法即可。该方案性能开销最低,适合绘制逻辑简单、绘制元素少的场景。
方案2:离屏渲染后硬缩放(复杂场景首选)
如果绘制逻辑复杂,逐一修改所有绘制参数成本太高,可以先把原始像素尺寸的内容绘制到离屏画布,生成图像后再以最近邻采样的方式缩放到目标尺寸,核心是给绘制图像的Paint设置filterQuality: FilterQuality.none,该参数会指定缩放时不做平滑插值,直接取最近像素点:
import 'dart:ui' as ui; class MyCustomPainter extends CustomPainter{ final double scaleFactor; final ui.Image originalImage; // 提前生成的原始16x16尺寸图像 MyCustomPainter(this.scaleFactor, this.originalImage); @override void paint(Canvas canvas, Size size) { // 将原始图像缩放到目标尺寸,使用最近邻采样 canvas.drawImageRect( originalImage, Rect.fromLTWH(0, 0, originalImage.width.toDouble(), originalImage.height.toDouble()), Rect.fromLTWH(0, 0, 16*scaleFactor, 16*scaleFactor), Paint() ..filterQuality = FilterQuality.none // 核心参数,关闭插值 ..isAntiAlias = false ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
注意:CustomPainter的paint方法不支持异步操作,原始尺寸图像的生成逻辑(
PictureRecorder录制、toImage异步转换)需要提前在State层的initState等生命周期中完成,再将生成的图像传入Painter使用。
该方案可以保留原有的原始尺寸绘制逻辑,不需要修改任何绘制参数,仅需要增加离屏渲染和缩放绘制的逻辑,适合绘制元素多、逻辑复杂的场景。
内容的提问来源于stack exchange,提问作者Gustavo Guzman
相关产品推荐
相关产品推荐

