HarmonyOS SkBitmap项目使用篇1 (回调数据SkBitmap给NativeWindow使用)

背景

将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

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

forevermgg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值