HarmonyOS APP开发---“滤镜大师“图像处理App,需要用到这个库

HarmonyOS APP开发---"滤镜大师"图像处理App,需要用到这个库

做一个滤镜 App,高斯模糊、边缘检测、色彩矩阵......这些算法用 ArkTS 跑太慢,必须用 C++ 写。但 NAPI 的样板代码让人头秃------@ohos/aki 一行绑定函数、一行绑定类,让 ArkTS 调 C++ 像调本地方法一样简单。
📦 仓库地址:https://gitcode.com/CPF-ApplicationTPC/aki | 安装:ohpm install @ohos/aki


写在前面

"滤镜大师"的核心是图像处理算法:高斯模糊、Sobel 边缘检测、色彩变换......这些算法要对每个像素做浮点运算,一张 1080p 图有 200 万像素,用 ArkTS 跑可能要好几秒,用 C++ 跑只需几十毫秒。

所以方案很明确:算法用 C++ 写,UI 用 ArkTS 写,中间用 NAPI 桥接。

但原生的 NAPI 写法极其繁琐------注册一个简单的 add(a, b) 函数,要写 napi_get_cb_infonapi_get_value_int32napi_create_int32 一堆样板。绑定一个 C++ 类?更是一场噩梦。

AKI (Alpha Kernel Interacting)就是来终结这些样板代码的------极简语法糖,一行绑定函数,一行绑定类

这篇文章聊什么

  1. C++ 滤镜算法------用 AKI 暴露给 ArkTS
  2. 绑定 C++ 类------滤镜引擎封装成对象
  3. Promise 异步处理------大图处理不阻塞 UI

#mermaid-svg-7i9wEp2ek07qDXsn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7i9wEp2ek07qDXsn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7i9wEp2ek07qDXsn .error-icon{fill:#552222;}#mermaid-svg-7i9wEp2ek07qDXsn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7i9wEp2ek07qDXsn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7i9wEp2ek07qDXsn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7i9wEp2ek07qDXsn .marker.cross{stroke:#333333;}#mermaid-svg-7i9wEp2ek07qDXsn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7i9wEp2ek07qDXsn p{margin:0;}#mermaid-svg-7i9wEp2ek07qDXsn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7i9wEp2ek07qDXsn .cluster-label text{fill:#333;}#mermaid-svg-7i9wEp2ek07qDXsn .cluster-label span{color:#333;}#mermaid-svg-7i9wEp2ek07qDXsn .cluster-label span p{background-color:transparent;}#mermaid-svg-7i9wEp2ek07qDXsn .label text,#mermaid-svg-7i9wEp2ek07qDXsn span{fill:#333;color:#333;}#mermaid-svg-7i9wEp2ek07qDXsn .node rect,#mermaid-svg-7i9wEp2ek07qDXsn .node circle,#mermaid-svg-7i9wEp2ek07qDXsn .node ellipse,#mermaid-svg-7i9wEp2ek07qDXsn .node polygon,#mermaid-svg-7i9wEp2ek07qDXsn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7i9wEp2ek07qDXsn .rough-node .label text,#mermaid-svg-7i9wEp2ek07qDXsn .node .label text,#mermaid-svg-7i9wEp2ek07qDXsn .image-shape .label,#mermaid-svg-7i9wEp2ek07qDXsn .icon-shape .label{text-anchor:middle;}#mermaid-svg-7i9wEp2ek07qDXsn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7i9wEp2ek07qDXsn .rough-node .label,#mermaid-svg-7i9wEp2ek07qDXsn .node .label,#mermaid-svg-7i9wEp2ek07qDXsn .image-shape .label,#mermaid-svg-7i9wEp2ek07qDXsn .icon-shape .label{text-align:center;}#mermaid-svg-7i9wEp2ek07qDXsn .node.clickable{cursor:pointer;}#mermaid-svg-7i9wEp2ek07qDXsn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7i9wEp2ek07qDXsn .arrowheadPath{fill:#333333;}#mermaid-svg-7i9wEp2ek07qDXsn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7i9wEp2ek07qDXsn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7i9wEp2ek07qDXsn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7i9wEp2ek07qDXsn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7i9wEp2ek07qDXsn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7i9wEp2ek07qDXsn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7i9wEp2ek07qDXsn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7i9wEp2ek07qDXsn .cluster text{fill:#333;}#mermaid-svg-7i9wEp2ek07qDXsn .cluster span{color:#333;}#mermaid-svg-7i9wEp2ek07qDXsn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7i9wEp2ek07qDXsn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7i9wEp2ek07qDXsn rect.text{fill:none;stroke-width:0;}#mermaid-svg-7i9wEp2ek07qDXsn .icon-shape,#mermaid-svg-7i9wEp2ek07qDXsn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7i9wEp2ek07qDXsn .icon-shape p,#mermaid-svg-7i9wEp2ek07qDXsn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7i9wEp2ek07qDXsn .icon-shape .label rect,#mermaid-svg-7i9wEp2ek07qDXsn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7i9wEp2ek07qDXsn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7i9wEp2ek07qDXsn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7i9wEp2ek07qDXsn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ArkTS: 用户选图
调用 C++ applyBlur
AKI 自动类型转换
C++: 高斯模糊算法
返回处理后的像素数据
AKI 转回 ArkTS
ArkTS: 渲染结果
用户对比前后


第一步:依赖配置

源码依赖(推荐):

bash 复制代码
cd entry/src/main/cpp
git clone https://gitcode.com/CPF-ApplicationTPC/aki.git

CMakeLists.txt:

cmake 复制代码
add_subdirectory(aki)
target_link_libraries(filter PUBLIC aki_jsbind)

第二步:C++ 滤镜算法 + AKI 绑定

先写一个简单的高斯模糊,用 AKI 一行绑定暴露给 ArkTS:

cpp 复制代码
// filter.cpp
#include <aki/jsbind.h>

// 高斯模糊:接收像素数组,返回处理后的数组
JSBIND_FUNCTION(applyGaussianBlur) {
    // AKI 自动把 ArkTS 参数转成 C++ 类型
    auto pixels = args[0].As<std::vector<uint8_t>>();  // 像素数据
    int width = args[1].As<int>();
    int height = args[2].As<int>();
    int radius = args[3].As<int>();

    // C++ 高斯模糊算法(省略实现细节)
    std::vector<uint8_t> result = gaussianBlur(pixels, width, height, radius);

    return result; // AKI 自动转回 ArkTS
}

// 注册到 ArkTS
JSBIND_ADDON(filter)

ArkTS 侧直接调用:

typescript 复制代码
import { applyGaussianBlur } from 'libfilter.so'

// ArkTS 调 C++,就像调本地函数一样
const blurred = applyGaussianBlur(pixelArray, width, height, 5)
// 渲染 blurred 到 Canvas

对比原生 NAPI :同样的功能,NAPI 要写 30+ 行样板代码,AKI 只要 1 行 JSBIND_FUNCTION。类型转换全自动,不用手写 napi_get_value_xxx


第三步:绑定 C++ 滤镜引擎类

把多个滤镜封装成一个 C++ 类,用 AKI 绑定:

cpp 复制代码
// FilterEngine 类:支持链式应用多个滤镜
class FilterEngine {
public:
    FilterEngine() : width_(0), height_(0) {}

    void setImage(std::vector<uint8_t> pixels, int width, int height) {
        pixels_ = pixels;
        width_ = width;
        height_ = height;
    }

    FilterEngine& applyBlur(int radius) {
        pixels_ = gaussianBlur(pixels_, width_, height_, radius);
        return *this; // 链式调用
    }

    FilterEngine& applyGrayscale() {
        pixels_ = toGrayscale(pixels_, width_, height_);
        return *this;
    }

    FilterEngine& applyBrightness(float factor) {
        pixels_ = adjustBrightness(pixels_, width_, height_, factor);
        return *this;
    }

    std::vector<uint8_t> getResult() { return pixels_; }

private:
    std::vector<uint8_t> pixels_;
    int width_, height_;
};

// AKI 绑定类:构造函数 + 成员方法
JSBIND_CLASS(FilterEngine) {
    JSBIND_CONSTRUCTOR();
    JSBIND_METHOD(setImage);
    JSBIND_METHOD(applyBlur);
    JSBIND_METHOD(applyGrayscale);
    JSBIND_METHOD(applyBrightness);
    JSBIND_METHOD(getResult);
}

JSBIND_ADDON(filter)

ArkTS 侧链式调用:

typescript 复制代码
import { FilterEngine } from 'libfilter.so'

const engine = new FilterEngine()
engine.setImage(pixelArray, 1920, 1080)
  .applyBlur(5)         // 高斯模糊
  .applyGrayscale()     // 转灰度
  .applyBrightness(1.2) // 提亮 20%

const result = engine.getResult()
// 渲染 result

第四步:Promise 异步处理

4K 大图处理可能要几百毫秒,用 Promise 避免阻塞 UI:

cpp 复制代码
JSBIND_FUNCTION(processImageAsync) {
    aki::Promise promise;

    std::thread([promise, args]() mutable {
        auto pixels = args[0].As<std::vector<uint8_t>>();
        // 耗时处理在子线程跑
        auto result = heavyProcess(pixels);
        promise.Resolve(result); // 完成后 resolve
    }).detach();

    return promise; // 返回 Promise 给 ArkTS
}
typescript 复制代码
// ArkTS 侧 await
const result = await processImageAsync(pixelArray)
console.info('处理完成')

为什么"滤镜大师"选了 AKI?

维度 原生 NAPI @ohos/aki
绑定函数 30+ 行样板 1 行 JSBIND_FUNCTION
绑定类 极其繁琐 JSBIND_CLASS + JSBIND_METHOD
类型转换 手写 napi_get/set 全自动
Promise 手写 AsyncWorker aki::Promise
线程安全 手写 ThreadSafeFunction 内置封装
代码量 100% ~40%

总结

"滤镜大师"这个场景里,@ohos/aki 解决了三件事:

  1. 函数绑定 ------一行 JSBIND_FUNCTION,C++ 算法直接暴露给 ArkTS
  2. 类绑定 ------JSBIND_CLASS 绑定滤镜引擎,链式调用无缝衔接
  3. 异步处理 ------aki::Promise 子线程跑算法,UI 不卡

如果你也需要在鸿蒙应用里复用 C/C++ 库、做高性能计算、调用底层能力,@ohos/aki 让你告别 NAPI 样板代码,把精力留给算法本身。

相关推荐
imaol12 小时前
链表 -- 环链表
java·前端·链表
imaol13 小时前
链表 -- 双向链表
java·前端·链表
2601_963869953 小时前
【计算机毕业设计】基于Java的相框定制系统的设计与实现
java·开发语言·课程设计
于烛4 小时前
为什么hasNext() 对只有一个元素的集合返回 true?90%初学者都有的疑问
java
wuminyu4 小时前
JVM利用io_uring优化堆外内存性能原理剖析
java·linux·c语言·jvm·c++
2501_919749034 小时前
华为鸿蒙管理密码APP—小羊密码
华为·harmonyos·鸿蒙
imaol14 小时前
数据结构---队列
java·数据结构·算法
半亩码田5 小时前
【.NET新特性·第11篇】C# 14 字段属性:告别手写 backing field
java·c#·.net
JAVA面经实录9175 小时前
集合框架 (六)
java·开发语言