鸿蒙开发-实战:用滤镜链做一个复古胶片滤镜
本文介绍了如何使用effectKit创建复古胶片风格的滤镜效果。文章详细讲解了实现复古胶片特征的关键技术点:通过颜色矩阵调整色调偏暖、降低对比度和饱和度,并添加轻微模糊效果。主要内容包括: 创建复古色调的颜色矩阵配置 构建滤镜处理流程:先提亮画面,再应用颜色矩阵,最后添加轻微模糊 完整的代码实现方案,包括图像加载、滤镜链创建和应用 颜色矩阵调参技巧,如调整权重实现偏暖色调、降低饱和度等效果 最终效
实战:用 effectKit 做一个"复古胶片"滤镜
今天我们来做点实际的——用 effectKit 的 Filter 链组合出一个复古胶片风格的滤镜。
复古胶片长什么样?泛黄的色调、稍微降低的对比度、一点点颗粒感、暗角。我们不需要全部做到(颗粒感需要像素级操作,effectKit 不直接支持),但色调和亮度调整完全可以。
复古滤镜处理流程
下面是复古胶片滤镜的完整处理流程:
复古滤镜的思路
复古胶片的核心特征:
- 色调偏暖——整体偏黄/偏橙
- 对比度降低——暗部提亮,亮部压暗
- 饱和度降低——颜色没那么鲜艳
- 轻微模糊——模拟老镜头的柔焦感
用 effectKit 怎么实现?
第一步:创建颜色矩阵
颜色矩阵是一个 5x4 的矩阵,可以同时控制色调、对比度、饱和度。复古风格的矩阵大概是这样的:
// 复古胶片矩阵
let vintageMatrix: Array<number> = [
0.6, 0.3, 0.1, 0, 20, // R: 偏暖,提亮
0.2, 0.7, 0.1, 0, 10, // G: 稍微偏绿
0.1, 0.2, 0.5, 0, 5, // B: 压低蓝色
0, 0, 0, 1, 0 // A: 不变
];
这个矩阵在说什么?每一行对应一个输出通道(R、G、B、A),前四列是输入通道的权重,最后一列是常量偏移。
以第一行为例:0.6, 0.3, 0.1, 0, 20 意味着:
- 输出的红色 = 输入红色×0.6 + 输入绿色×0.3 + 输入蓝色×0.1 + 20
为什么要这样设?复古胶片偏暖(偏黄/橙),所以我们压低蓝色(B 通道权重只有 0.5),同时给红色通道加了 20 的常量偏移,让暗部也带点暖色。
第二步:创建滤镜链
import { image } from '@kit.ImageKit';
import { effectKit } from '@kit.ArkGraphics2D';
先导入需要的模块。image 用来处理图片,effectKit 提供滤镜功能。
let imageSource = image.createImageSource(source);
let pixelMap = await imageSource.createPixelMap();
把图片数据转换成 PixelMap——这是 HarmonyOS 处理图片的标准格式,所有图像操作都要基于它。
let headFilter = effectKit.createEffect(pixelMap);
createEffect 创建一个滤镜效果对象,传入 PixelMap。返回的 headFilter 是滤镜链的"头节点",后续所有滤镜都挂在这个链上。
第三步:添加滤镜
if (headFilter != null) {
// 第一步:轻微提亮
headFilter.brightness(0.1);
brightness(0.1) 把整体亮度提高 10%。复古胶片不会太暗,但也不能太亮,0.1 是个保守的值。
// 第二步:复古色调矩阵
headFilter.setColorMatrix(vintageMatrix);
setColorMatrix 应用我们刚才定义的颜色矩阵。这一步是复古效果的核心——它改变了整个画面的色调。
// 第三步:轻微柔焦
headFilter.blur(1);
}
blur(1) 加一点模糊,模拟老镜头的柔焦感。参数 1 表示模糊半径为 1 像素,效果很轻微。
注意顺序:先提亮,再调色,最后柔焦。如果先柔焦再调色,颜色矩阵会作用在已经模糊的像素上,效果可能不太对。
第四步:获取结果
headFilter.getEffectPixelMap().then(result => {
resolve(result);
})
getEffectPixelMap() 把滤镜链应用到图片上,返回处理后的 PixelMap。这个操作是异步的,因为图像处理需要时间。
完整流程
把上面的步骤串起来:
function applyVintageFilter(source: ArrayBuffer): Promise<image.PixelMap> {
return new Promise(async (resolve, reject) => {
let imageSource = image.createImageSource(source);
await imageSource.createPixelMap().then(async (pixelMap: image.PixelMap) => {
let headFilter = effectKit.createEffect(pixelMap);
if (headFilter != null) {
headFilter.brightness(0.1); // 提亮
headFilter.setColorMatrix(vintageMatrix); // 复古色调
headFilter.blur(1); // 柔焦
}
headFilter.getEffectPixelMap().then(result => {
resolve(result);
})
})
})
}
在页面中使用
@Entry
@Component
struct VintageFilter {
@State originalMap: image.PixelMap | null = null;
@State vintageMap: image.PixelMap | null = null;
定义两个状态变量:原图和复古效果。
async aboutToAppear() {
const context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const fileData: Uint8Array = await context.resourceManager.getRawFileContent('photo.jpg');
const buffer: ArrayBuffer = fileData.buffer.slice(0);
从 rawfile 文件夹加载图片。getRawFileContent 返回 Uint8Array,我们需要转成 ArrayBuffer 才能用。
// 保存原图
let imageSource = image.createImageSource(buffer);
this.originalMap = await imageSource.createPixelMap();
// 应用复古滤镜
this.vintageMap = await applyVintageFilter(buffer);
}
同时保存原图和处理后的图,方便对比。
build() {
Column() {
Text('复古胶片滤镜')
.fontSize(24)
.margin(20)
// 原图
if (this.originalMap) {
Text('原图')
.fontSize(16)
.margin({ bottom: 10 })
Image(this.originalMap)
.width('90%')
.aspectRatio(1.5)
}
// 复古效果
if (this.vintageMap) {
Text('复古效果')
.fontSize(16)
.margin({ top: 20, bottom: 10 })
Image(this.vintageMap)
.width('90%')
.aspectRatio(1.5)
}
}
.width('100%')
.height('100%')
}
}
布局很简单:上面显示原图,下面显示复古效果。aspectRatio(1.5) 让图片保持 3:2 的比例。
颜色矩阵调参思路
颜色矩阵的每个值都会影响最终效果:
调参技巧
颜色矩阵不是随便写的,每个值都有含义。这里分享一些调参技巧:
想让画面偏暖? 增大第一行的常量偏移(最后一个值),同时增大 R 通道对 R 输入的权重。
想降低饱和度? 让每行的三个权重接近(R≈G≈B),这样输出的颜色就会趋向灰色。
想增加对比度? 让权重之和大于 1,同时用负的常量偏移补偿。
想做冷色调? 增大 B 通道的权重和偏移,压低 R 通道。
你可以试着改改矩阵里的值,看看效果有什么变化。这种"手动调色"的过程其实很有趣——就像在调一台老式电视机的旋钮。
滤镜链组合方式
effectKit支持多种滤镜叠加使用:
小结
复古胶片滤镜的核心就是:
- 颜色矩阵调整色调(偏暖、降饱和度)
- brightness 微调亮度
- blur(1) 加一点柔焦
三个 Filter 叠加,效果已经很不错了。如果你想更精细,可以再加上 grayscale() 做部分去色,或者用不同的颜色矩阵做更多风格化效果。
更多推荐



所有评论(0)