你有没有注意到,有时候把一张小图放大显示,会变得很模糊或者有很多锯齿?而有时候放大后虽然也不够清晰,但至少边缘比较平滑?这背后的原因就是"采样方式"不同。
在 HarmonyOS 的 drawing 模块里,SamplingOptions就是用来控制图片缩放时的采样质量的。简单说,它决定了当你把一张图片放大或缩小显示时,系统用什么算法来计算每个像素的颜色。
什么是采样?
下面是两种采样模式的选择决策流程:
打个比方:你有一张 100x100 的小图片,想把它显示在 200x200 的区域里。这时候图片需要"放大",多出来的那些像素该填什么颜色?这就是"采样"要解决的问题。
不同的采样算法会产生不同的效果:
- 有的算法追求速度,放大会有很多锯齿
- 有的算法追求质量,放大会比较平滑但可能有点模糊
创建 SamplingOptions
默认构造:
import{RenderNode}from'@kit.ArkUI';import{common2D,drawing}from'@kit.ArkGraphics2D';classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvas=context.canvas;constpen=newdrawing.Pen();letsamplingOptions=newdrawing.SamplingOptions();}}无参构造函数创建的 SamplingOptions,默认使用FILTER_MODE_NEAREST(最近邻采样)。
指定过滤模式构造:
import{RenderNode}from'@kit.ArkUI';import{common2D,drawing}from'@kit.ArkGraphics2D';classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvas=context.canvas;letsamplingOptions=newdrawing.SamplingOptions(drawing.FilterMode.FILTER_MODE_NEAREST);}}你也可以在构造时直接指定过滤模式。
FilterMode:过滤模式
FilterMode是一个枚举,目前有两个值:
FILTER_MODE_NEAREST(最近邻采样)
这是默认的模式。它的原理很简单:放大图片时,每个新像素直接复制离它最近的原始像素的颜色。
优点:速度最快,不会引入额外的模糊。
缺点:放大后会有明显的锯齿和"马赛克"效果,特别是放大倍数比较大的时候。
适用场景:
- 像素风格的游戏(故意要那种"马赛克"效果)
- 需要保持锐利边缘的图标放大
- 对性能要求很高的实时渲染
FILTER_MODE_LINEAR(线性采样)
线性采样会考虑周围多个像素的颜色,然后做加权平均。
优点:放大后比较平滑,没有明显锯齿。
缺点:速度比最近邻稍慢,可能会让图片看起来有点"模糊"。
适用场景:
- 照片类图片的缩放
- 需要平滑效果的 UI 元素
- 对画质要求比较高的场景
怎么使用 SamplingOptions?
SamplingOptions 通常是在绘制图片时配合 Canvas 的方法使用的。比如在调用canvas.drawImageRect之类的方法时,传入 SamplingOptions 来控制采样方式:
import{RenderNode}from'@kit.ArkUI';import{common2D,drawing}from'@kit.ArkGraphics2D';classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvas=context.canvas;// 创建采样选项,使用线性采样letsamplingOptions=newdrawing.SamplingOptions(drawing.FilterMode.FILTER_MODE_LINEAR);// 假设你有一个 pixelMap// canvas.drawImageRect(pixelMap, srcRect, dstRect, samplingOptions);}}在绘制的时候,系统会根据你设置的 SamplingOptions 来决定用什么算法进行像素采样。
两种模式的视觉区别
让我用更具体的方式来说明两种模式的区别:
放大一个小图标(比如 16x16 放大到 64x64):
- NEAREST:你会看到明显的"像素块",每个原始像素变成了 4x4 的方块。边缘是"阶梯状"的。
- LINEAR:边缘会比较平滑,像素之间的过渡更自然,但整体可能看起来有点"糊"。
缩小一张大图片(比如 1000x1000 缩小到 100x100):
- NEAREST:可能会出现"摩尔纹"(一种奇怪的波纹图案),因为很多像素被直接跳过了。
- LINEAR:缩小后的图片比较平滑,不会有奇怪的纹路。
实际应用场景
根据应用场景选择采样模式:
场景一:像素风格游戏
如果你在做一个复古风格的像素游戏,角色图片很小(比如 32x32),需要放大显示。这时候用 NEAREST 可以保持那种"像素感":
letsamplingOptions=newdrawing.SamplingOptions(drawing.FilterMode.FILTER_MODE_NEAREST);// 放大绘制像素风格的角色图片场景二:照片查看器
做一个照片查看 APP,用户可以放大缩小查看照片。这时候用 LINEAR 可以让缩放过程更平滑:
letsamplingOptions=newdrawing.SamplingOptions(drawing.FilterMode.FILTER_MODE_LINEAR);// 缩放绘制照片场景三:地图渲染
地图瓦片在不同缩放级别下需要不同的采样策略。缩小时用 LINEAR 避免摩尔纹,放大到一定程度切换到 NEAREST 保持清晰。
场景四:头像裁剪
用户裁剪头像时,预览区域需要放大显示。用 LINEAR 可以让预览看起来更平滑,用户体验更好。
性能考虑
NEAREST 模式的性能比 LINEAR 好,因为它的计算更简单。在以下情况建议用 NEAREST:
- 需要高频绘制(比如游戏的每帧渲染)
- 图片本身就是像素风格的
- 对性能要求极高的场景
在以下情况建议用 LINEAR:
- 绘制照片或自然图像
- 用户可以看到缩放过程(比如手势缩放)
- 对画质要求高于性能
注意事项
API 版本:SamplingOptions 从 API version 12 开始支持。
物理像素:和其他 drawing 对象一样,使用物理像素单位。
线程安全:单线程模型,需要自己管理线程安全。
默认值:无参构造默认使用 NEAREST 模式。
和 BitmapShader 配合:SamplingOptions 也可以在创建 BitmapShader 时使用,控制纹理采样的质量。
小结
SamplingOptions 是一个简单但重要的工具,专门控制图片缩放时的采样质量。它只有两个选择:
- NEAREST:速度快,保持锐利,但可能有锯齿
- LINEAR:速度稍慢,效果平滑,但可能有点模糊
根据你的具体需求选择合适的模式就好。大多数情况下,LINEAR 是更安全的选择,除非你特别需要那种"像素风"的效果或者对性能有极致要求。