HarmonyOS 6 轻相机应用开发2:贴纸效果实现

HarmonyOS 6 轻相机应用开发2:贴纸效果实现

引言

在上一篇《功能介绍与框架搭建》中,我们确立了"预览+推理"的双路流架构。如果说"实时滤镜"是相机的外衣,那么"实时贴纸"就是它的灵魂。

贴纸效果本质上是在相机预览流之上,叠加了一层动态的图像信息。在 HarmonyOS 6 中,为了保证 60fps 的极致流畅度,我们将舍弃开销较大的 ArkTS Canvas 绘制方案,转而深入 Native NDK 层,利用 Native Drawing (OH_Drawing) 与 OpenGL ES 纹理混合技术,构建一条全链路的 C++ 渲染流水线。

贴纸实现基本原理

贴纸的实现流程可以概括为以下三个核心步骤:

  1. 资源加载与解码 :从应用的 rawfile 目录读取 PNG/JPG 图像并解码为像素数据。
  2. 离屏绘制 :利用 Native Drawing 接口在内存中创建一个透明位图,根据 AI 检测到的坐标(如面部描点)将贴纸素材绘制上去。
  3. 纹理融合 :将位图上传为 OpenGL 2D 纹理,在 Fragment Shader 中与相机的 OES 纹理进行 Alpha 混合(Alpha Blending)。
NDK 实战:从 rawfile 读取并解析 PNG

在 Native 层直接处理图片资源,可以极大减少内存拷贝。我们需要用到 ResourceManager 来访问资源,并配合 ImageSource NDK 接口进行解码。

以下是实现这一链路的关键 C++ 代码(需链接 libimage_source.so 与 librawfile.z.so):

cpp 复制代码
#include <multimedia/image_framework/image_source_native.h>
#include <rawfile/raw_file_manager.h>

// 从 rawfile 中加载贴纸位图
OH_PixelmapNative* LoadStickerFromRawFile(NativeResourceManager* resMgr, const char* fileName) {
    // 1. 打开 RawFile 资源
    RawFile* rawFile = OH_ResourceManager_OpenRawFile(resMgr, fileName);
    if (rawFile == nullptr) return nullptr;

    // 2. 创建 ImageSourceNative
    long rawFileSize = OH_ResourceManager_GetRawFileSize(rawFile);
    OH_ImageSourceNative* imageSource = nullptr;
    // 注意:需先获取 fd 或者直接从内存创建,这里推荐使用 CreateFromRawFile
    Image_ErrorCode ret = OH_ImageSourceNative_CreateFromRawFile(resMgr, (char*)fileName, strlen(fileName), &imageSource);

    if (ret != IMAGE_SUCCESS) {
        OH_ResourceManager_CloseRawFile(rawFile);
        return nullptr;
    }

    // 3. 解码为 Pixelmap
    OH_DecodingOptions* opts = nullptr; // 可设置采样率等选项
    OH_PixelmapNative* pixelmap = nullptr;
    OH_ImageSourceNative_CreatePixelmap(imageSource, opts, &pixelmap);

    // 4. 清理资源
    OH_ImageSourceNative_Release(imageSource);
    OH_ResourceManager_CloseRawFile(rawFile);

    return pixelmap;
}

解码后的 OH_PixelmapNative 对象随后可被绑定到 OH_Drawing_Bitmap 中,作为绘制源。

Native Drawing (OH_Drawing) 绘制逻辑

Native Drawing 是 HarmonyOS 提供的一套高性能 2D 绘图接口。在我们的方案中,它承担了"离屏画布"的角色:

  • 创建画布 :利用 OH_Drawing_BitmapCreate 创建一个与预览分辨率匹配(或比例一致)的透明位图。
  • 绑定 Canvas :使用 OH_Drawing_CanvasBind 将画布与位图关联。
  • 执行绘制 :根据 AI 推理回传的坐标,调用 OH_Drawing_CanvasDrawPixelMapRect 将贴纸素材绘制到指定位置。

由于这一过程全部在 C++ 层内存中完成,不涉及跨语言的大数据拷贝,因此即使是复杂的动态贴纸,也能保持极高的帧率。

OpenGL 纹理混合与 ALPHA_BLEND 数学公式

这是本章最核心的理论部分。相机采集的是 OES 纹理(通常是不透明的 YUV 转 RGB 流),而贴纸是带 Alpha 通道的 RGBA 纹理。我们需要在 Fragment Shader 中将二者进行融合。

1. ALPHA_BLEND 数学模型

在图形学中,最常用的混合算法是"源覆盖(Source Over)"模型。其数学表达式如下:

Cout=Csrc×Asrc+Cdst×(1−Asrc)C_{out} = C_{src} \times A_{src} + C_{dst} \times (1 - A_{src})Cout=Csrc×Asrc+Cdst×(1−Asrc)

其中:

  • CsrcC_{src}Csrc:贴纸(源)的颜色值。
  • AsrcA_{src}Asrc:贴纸的透明度(0.0 到 1.0)。
  • CdstC_{dst}Cdst:预览画面(目标/背景)的颜色值。
  • CoutC_{out}Cout:最终混合后的颜色输出。

物理意义 :当贴纸某一点全透明(Asrc=0A_{src}=0Asrc=0)时,输出结果完全由背景决定;当其全不透明(Asrc=1A_{src}=1Asrc=1)时,则完全显示贴纸。

2. Shader 混合代码实现

在 render_thread.cpp 的片段着色器中,我们可以直接落实这一公式:

glsl 复制代码
// 片段着色器片段
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 vTexCoord;
uniform samplerExternalOES cameraTexture; // 相机 OES 纹理
uniform sampler2D stickerTexture;        // 贴纸 RGBA 纹理

void main() {
    vec4 cameraColor = texture2D(cameraTexture, vTexCoord);
    vec4 stickerColor = texture2D(stickerTexture, vTexCoord);

    // 应用 Alpha 混合公式
    float r = stickerColor.r * stickerColor.a + cameraColor.r * (1.0 - stickerColor.a);
    float g = stickerColor.g * stickerColor.a + cameraColor.g * (1.0 - stickerColor.a);
    float b = stickerColor.b * stickerColor.a + cameraColor.b * (1.0 - stickerColor.a);

    gl_FragColor = vec4(r, g, b, 1.0);
}
渲染流水线深度剖析

为了实现"语义化"的贴纸效果,我们将全链路串联如下:

  1. AI 检测阶段:第一路 AI 流探测到面部 106 点坐标。
  2. 坐标转换:将模型输出的归一化坐标转换至视图坐标系。
  3. 离屏绘制 (Off-screen) :OH_Drawing 在后台 Bitmap 上开启"透明涂层",绘制贴纸。
  4. 纹理上传 :通过 glTexImage2D 将绘图后的内存数据上传至 GPU 纹理。
  5. 并轨渲染 :OpenGL 执行双采样 Shader,最后将结果刷新至 XComponent 的 Surface 屏幕上。
总结

通过本章的实践,我们不仅掌握了 NDK 层级图片处理的硬核技能,还理解了底层图形渲染的数学之美。贴纸不再是简单的 UI 堆栈,而是深度融合进渲染管线的图像算子。在下一篇中,我们将探讨**"智慧物品检测"**,教你如何让相机不仅能"看面子",还能"认里子"。

相关推荐
Token_w2 小时前
2026鸿蒙平板远控电脑怎么选?大屏适配、自动校准等适配完整度逐项实测
华为·harmonyos
allnlei2 小时前
Opus In-Band FEC 详解:原理、实现与开源方案
ffmpeg·音视频·gstreamer
爱吃提升3 小时前
文生视频模型发展趋势(2026)
人工智能·音视频
梦想不只是梦与想3 小时前
鸿蒙应用市场:版本升级
harmonyos·应用升级·签名不一致
resh_people4 小时前
开源鸿蒙平台 KMP_CMP 三方库「WhatIf」适配全流程
华为·开源·harmonyos
可乐鸡翅yeah_4 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
小雨青年5 小时前
【HarmonyOS 7 闪控窗深度实战】04 闪控窗调整窗口形态与显示区域
华为·harmonyos
李游Leo6 小时前
HarmonyOS 7 + ArkUI + RelationalStore 学习笔记:本地数据分层存储与状态持久化架构实践【鸿蒙心迹】
笔记·学习·架构·harmonyos
apple_692658856 小时前
最新ComfyUI 0.37.1使用教程:画图、3D、音频都有改动 ComfyUI升级到0.37.1啦,新增或改进五大功能
音视频
李游Leo7 小时前
《HarmonyOS 7 精准碰一碰跨设备协作开发实战》07:异常恢复、状态机与CrossDrop工程收尾【鸿蒙心迹】
android·harmonyos