背景
将sdk生成的有价值数据,copy多份生成到其他View上
知识储备(jnapi + native_window的使用)
- https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NativeXComponent
- https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-common-components-xcomponent-0000001504835025-V2
- https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NaitveTemplate
- https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/_o_h___native_x_component___callback-0000001497370437-V2
回调void * data给其他地方使用
#include <include/core/SkCanvas.h>
#include <include/core/SkBitmap.h>
#include <include/core/SkPaint.h>
#include <include/core/SkTypeface.h>
#include <include/core/SkFont.h>
#include <include/codec/SkCodec.h>
#include <include/core/SkStream.h>
#include <include/encode/SkPngEncoder.h>
#include <iostream>
#include "../file.h"
int main(int argc, char *const argv[]) {
std::filesystem::path current_path = std::filesystem::current_path();
std::string current_directory = current_path.parent_path().parent_path();
std::cout << "当前目录是: " << current_directory << std::endl;
// 1:第一阶段 创建格外的Canvas
SkBitmap m_bitmap;
m_bitmap.rowBytes(); // size_t
m_bitmap.getPixels(); // void*
int canvas_width = 100;
int canvas_height = 100;
std::string canvas_data;
size_t nRowBytes = 0;
bool need_unpremul = false;
constexpr SkColorType kDefaultColorType = kRGBA_8888_SkColorType;
size_t length = canvas_width * canvas_height * 4;
canvas_data.reserve(length);
std::fill(canvas_data.begin(), canvas_data.end(), '\0');
SkImageInfo image_info = SkImageInfo::Make(canvas_width, canvas_height, kDefaultColorType, need_unpremul ? SkAlphaType::kUnpremul_SkAlphaType : SkAlphaType::kOpaque_SkAlphaType);
m_bitmap.setInfo(image_info, nRowBytes != 0 ? nRowBytes : image_info.minRowBytes());
m_bitmap.setPixels(canvas_data.data());
// 2:第二阶段 绘制数据
// 创建画布
SkCanvas canvas(m_bitmap);
// 创建画笔
SkPaint paint;
canvas.clear(SkColorSetARGB(0xFF, 0x14, 0x14, 0x14));
// 将画布清空并填充一种颜色,注意这里是ARGB,Alpha通道值在第一位,同时可以直接用16进制数表示,例如上面这个颜色值可以表示为0xFF141414
paint.setColor(SK_ColorWHITE);
// 创建字体设备
SkFont font;
// 设置字体尺寸
font.setSize(14);
SkString text("Hello, Skia!");
//在画布上绘制字体
canvas.drawSimpleText(text.c_str(), text.size(), SkTextEncoding::kUTF8, 0, 64, font, paint);
// 3:第三阶段 生成回调数据
void * data = m_bitmap.getPixels();
int width = canvas.imageInfo().width();
int height = canvas.imageInfo().height();
size_t rowBytes = nRowBytes;
SkColorType colorType = SkColorType::kRGBA_8888_SkColorType;
SkAlphaType alphaType = need_unpremul ? SkAlphaType::kUnpremul_SkAlphaType : SkAlphaType::kOpaque_SkAlphaType;
// 4:第四阶段 获取数据恢复SkBitmap
SkBitmap bitmap;
SkImageInfo imageInfo = SkImageInfo::Make(width, height, colorType, alphaType);
bitmap.setInfo(imageInfo, rowBytes);
bitmap.setPixels(data);
// 5:第五阶段 保存数据,查看结果。
SkFILEWStream file((current_directory + PATH_SEPARATOR + "bitmap_use.png").c_str()); //创建文件输出流
if (!file.isValid()) {
return 1;
}
return SkPngEncoder::Encode(&file, bitmap.pixmap(), {});
}
使用 native_window 构筑画布,将void * data绘制数据
- 1.根据BufferHandle根据生成SkBitmap;
- 2.根据void * data还原SkBitmap
// 4:第四阶段 获取数据恢复SkBitmap
SkBitmap bitmap;
SkImageInfo imageInfo = SkImageInfo::Make(width, height, colorType, alphaType);
bitmap.setInfo(imageInfo, rowBytes);
bitmap.setPixels(data);
- 3.根据恢复的SkBitmap获取SkImage,并将其绘制的含有BufferHandle virAddr的SkBitmap上。
canvas->drawImageRect(
bitmap.asImage(),
SkRect::MakeIWH(bufferHandle->width, bufferHandle->height),
SkSamplingOptions());
void OnSurfaceCreatedCB(NativeXComponent* component, void* window) {
//获取 native window 的宽高
uint64_t width_ = 0, height_ = 0;
int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return;
}
// 可获取 OHNativeWindow 实例,将void* 转换为 NativeWindow 实例,NativeWindow 定义在 native_window/external_window.h 中
OHNativeWindow* nativeWindow = static_cast<OHNativeWindow*>(window);
NativeWindow* nativeWindow_ = (NativeWindow*)(window);
// 通过 OH_NativeWindow_NativeWindowHandleOpt 设置或者获取NativeWindow的属性
// 1.通过 SET_USAGE 设置 Native Window 的 usage 属性, 例如:HBM_USE_CPU_READ
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_USAGE, HBM_USE_CPU_READ | HBM_USE_CPU_WRITE |HBM_USE_MEM_DMA);
// 2.通过 SET_BUFFER_GEOMETRY 设置 Native Window 的 宽高属性
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_BUFFER_GEOMETRY, width_, height_);
// 3.通过 SET_FORMAT 设置 Native Window 的 format 属性, 例如:PIXEL_FMT_RGBA_8888
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_FORMAT, PIXEL_FMT_RGBA_8888);
// 4.通过 SET_STRIDE 设置 Native Window 的 stride 属性
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_STRIDE, 0x8);
// 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 NativeWindowBuffer 实例
struct NativeWindowBuffer* buffer = nullptr;
int fenceFd;
OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer, &fenceFd);
// 通过 OH_NativeWindow_GetNativeBufferHandleFromNative 获取 buffer handle
BufferHandle* bufferHandle = OH_NativeWindow_GetNativeBufferHandleFromNative(buffer);
if (bufferHandle == nullptr) {
return;
}
// 通过 BufferHandle 创建Skia Bitmap
SkBitmap bitmap;
SkImageInfo imageInfo = ...
bitmap.setInfo(imageInfo, bufferHandle->stride);
bitmap.setPixels(bufferHandle->virAddr);
//创建 Skia Canvas 并将内容写入native window
...
// 设置刷新区域,如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
Region region{nullptr, 0};
// 写入完成后,通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上
OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer, fenceFd, region)
}
/*将 sk_sp backing_store 画布中的数据 绘制到 native_window 构筑的画布中 */
static std::unique_ptr<SkCanvas> MakeRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes, const SkSurfaceProps* props = nullptr);
bool GetSkColorType(int32_t buffer_format,
SkColorType* color_type,
SkAlphaType* alpha_type) {
switch (buffer_format) {
case PIXEL_FMT_RGBA_8888: // PIXEL_FMT_RGBA_8888
*color_type = kRGBA_8888_SkColorType;
*alpha_type = kPremul_SkAlphaType;
return true;
default:
return false;
}
}
{
SkPixmap pixmap;
if (!backing_store->peekPixels(&pixmap)) {
return;
}
...
void* virAddr = mmap(nullptr, bufferHandle->size, PROT_READ | PROT_WRITE,
MAP_SHARED, bufferHandle->fd, 0);
if (virAddr == MAP_FAILED) {
OH_NativeWindow_DestroyNativeWindowBuffer(buffer);
return ;
}
SkColorType color_type;
SkAlphaType alpha_type;
if (GetSkColorType(bufferHandle->format, &color_type, &alpha_type)) {
SkImageInfo native_image_info = SkImageInfo::Make(
bufferHandle->width, bufferHandle->height, color_type, alpha_type);
int bytesPerPixel = 1; // SkColorTypeBytesPerPixel(color_type);
std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(
native_image_info, virAddr, bufferHandle->stride * bytesPerPixel);
if (canvas) {
SkBitmap bitmap;
if (bitmap.installPixels(pixmap)) {
canvas->drawImageRect(
bitmap.asImage(),
SkRect::MakeIWH(bufferHandle->width, bufferHandle->height),
SkSamplingOptions());
} else {
}
} else {
}
} else {
}
Region region{nullptr, 0};
if (virAddr != nullptr) {
munmap(virAddr, bufferHandle->size);
}
ret = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);
OH_NativeWindow_DestroyNativeWindowBuffer(buffer);
}
参考
https://github.com/thelou1s/flutter_engine/blob/a23dc9a89e5e794ebf13d94a948a2ffccb7a4c09/shell/platform/ohos/ohos_surface_software.cpp#L241
https://code.opensource.huaweicloud.com/openharmony/docs/files?ref=OpenHarmony-v3.2-Beta3&tab=content&filePath=zh-cn%2Fapplication-dev%2Fnapi%2Fnative-window-guidelines.md&isFile=true
https://github.com/SMAT-Lab/SE4OpenHarmony/blob/0ce02354d432417350c16c6e50d70b58a94e6c71/Apps/OHApps/0_show_case/entry/src/main/cpp/hello.cpp