场景描述

部分用户需要在Native侧实现对PixelMap进行图形处理。因此本文详细讲述了如何在Native侧操作PixelMap,包含对图片的旋转裁剪等。

方案描述

  1. 在ArkTS侧:
    1. 在Index.ets文件中添加XComponent组件,使用XComponent组件来送显图片。
    2. 通过资源管理器获取图片并解码成PixelMap对象。
  2. 在Native侧使用c++对PixelMap进行数据处理:
    1. 创建PluginManager单例来管理XComponent组件。
    2. 创建SampleBitMap类,一个XComponent组件id对应一个SampleBitMap实例,所有对PixelMap对象的处理和Drawing的渲染输出操作都在该类中实现。

Native侧操作PixelMap的所有功能:

基础功能

API能力支持

Alpha通道

OH_PixelMap_SetAlphaAble

像素密度

OH_PixelMap_SetDensity

透明度

OH_PixelMap_SetOpacity

缩放

OH_PixelMap_Scale

偏移

OH_PixelMap_Translate

旋转

OH_PixelMap_Rotate

翻转

OH_PixelMap_Flip

裁剪

OH_PixelMap_Crop

场景实现

  • 实现PixelMap图片的基础功能
  1. TS侧准备。
    1. 在aboutToAppear方法中创建PixelMap对象。
    2. 调用c++的transform接口,传入PixelMap对象。
  2. Native侧准备。
    1. 接收数据后调用OH_PixelMap_Rotate和OH_PixelMap_Crop等方法对图片进行处理。
    2. 调用Drawing的OH_Drawing_CanvasDrawPixelMapRect方法将修改后的PixelMap图形渲染输出。

案例代码

import image from '@ohos.multimedia.image'
import XComponentContext from "../interface/XComponentContext";

const TAG = '[Sample_DrawingAPI]';

@Entry
@Component
struct Index {
  private xComponentContext: XComponentContext | undefined = undefined;
  private arr: number[] = [0, 1, 2, 3, 4, 5];
  private arrStr: string[] = ["重置", "缩放", "偏移", "旋转", "翻转", "裁剪"];
  @State _pixelMap: image.PixelMap | undefined = undefined;

  //重新设置图片初始状态
  async resetPixelMap() {
    let resourceManager = getContext(this).resourceManager
    let imageArray = await resourceManager.getMediaContent($r('app.media.beer'));
    let imageResource = image.createImageSource(imageArray.buffer);
    let opts: image.DecodingOptions = { editable: true }
    this._pixelMap = await imageResource.createPixelMap(opts);
  }

  async aboutToAppear() {
    this.resetPixelMap();
  }

  //PixelMap基础操作
  pixelMapTransform(opType: number) {
    if (this.xComponentContext && this._pixelMap) {
      this.xComponentContext.transform(this._pixelMap, opType);
    }
  }

  build() {
    Column() {
      Row() {
        XComponent({ id: 'xcomponentId', type: 'surface', libraryname: 'entry' })
          .onLoad((xComponentContext) => {
            this.xComponentContext = xComponentContext as XComponentContext;
          }).width('960px') // Multiples of 64
      }.height('70%')

      Row() {
        //场景一:基础操作
        List({ space: 8, initialIndex: 0 }) {
          ForEach(this.arr, (item: number) => {
            ListItem() {
              Text('' + this.arrStr[item])
                .width('80%')
                .height(30)
                .fontSize(14)
                .textAlign(TextAlign.Center)
                .borderRadius(5)
                .backgroundColor(0xFFFF85)
                .onClick(() => {
                  console.log(TAG, "操作:" + this.arrStr[item]);
                  if (item == 0) {
                    this.resetPixelMap().then(() => {
                      this.pixelMapTransform(item);
                    })
                  } else {
                    this.pixelMapTransform(item);
                  }
                })
            }
          }, (item: number) => item.toString())
        }.width('30%').height('96%')

        //场景二:扩展操作
        Button('PixelMap扩展')
          .fontSize('14fp')
          .fontWeight(500)
          .margin({ bottom: 24, right: 12 })
          .onClick(() => {
            if (this.xComponentContext && this._pixelMap) {
              this.xComponentContext.drawPattern(this._pixelMap);
            }
          })
          .width('40%')
          .height(40)
          .shadow(ShadowStyle.OUTER_DEFAULT_LG)
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
      .shadow(ShadowStyle.OUTER_DEFAULT_SM)
      .alignItems(VerticalAlign.Bottom)
      .layoutWeight(1)
    }
  }
}
static void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
{
    DRAWING_LOGI("OnSurfaceCreatedCB");
    if ((component == nullptr) || (window == nullptr)) {
        DRAWING_LOGE("OnSurfaceCreatedCB: component or window is null");
        return;
    }
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        DRAWING_LOGE("OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }
    std::string id(idStr);
    auto render = SampleBitMap::GetInstance(id);
    OHNativeWindow *nativeWindow = static_cast<OHNativeWindow *>(window);
    render->SetNativeWindow(nativeWindow);
    uint64_t width;
    uint64_t height;
    int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
        render->SetHeight(height);
        render->SetWidth(width);
        DRAWING_LOGI("xComponent width = %{public}llu, height = %{public}llu", width, height);
    }
}
napi_value SampleBitMap::NapiTransform(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    napi_value argValue[2] = {0,0};
    size_t argCount = 2;
    if (napi_get_cb_info(env, info, &argCount, argValue, &thisVar, nullptr) != napi_ok || argCount &lt; 2 ||
        argValue[0] == nullptr || argValue[1] == nullptr) {
        return nullptr;
    }
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    // 初始化PixelMap对象数据。
    NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, argValue[0]);
    if (native == nullptr) {
        return nullptr;
    }
    
    //获取操作类型
    int32_t opType;
    napi_get_value_int32(env, argValue[1], &opType);
    
    // 获取图片信息。
    struct OhosPixelMapInfos pixelmapInfo;
    OH_PixelMap_GetImageInfo(native, &pixelmapInfo);
    // 获取PixelMap对象每行字节数。
    int32_t rowBytes;
    OH_PixelMap_GetBytesNumberPerRow(native, &rowBytes);
    // 获取PixelMap对象是否可编辑的状态。
    int32_t editable = 0;
    OH_PixelMap_GetIsEditable(native, &editable);
    // 获取PixelMap对象是否支持Alpha通道。
    int32_t supportAlpha = 0;
    OH_PixelMap_IsSupportAlpha(native, &supportAlpha);
    // 设置PixelMap对象的Alpha通道。
    int32_t alphaAble = 0;
    OH_PixelMap_SetAlphaAble(native, alphaAble);
    // 获取PixelMap对象像素密度。
    int32_t densityG;
    OH_PixelMap_GetDensity(native, &densityG);
    // 设置PixelMap对象像素密度。
    int32_t densityS = 100;
    OH_PixelMap_SetDensity(native, densityS);
    // 设置PixelMap对象的透明度。
    //float opacity = 0.6;
    //OH_PixelMap_SetOpacity(native, opacity);

    // 设置缩放比例。(ok)
    // scaleX: 宽为原来的0.5。
    // scaleY: 高为原来的0.5。
    if(opType == OP_TYPE_SCALE){
        float scaleX = 0.1;
        float scaleY = 0.1;
        OH_PixelMap_Scale(native, scaleX, scaleY);
    }

    // 设置偏移。(ok)
    // translateX: 向下偏移50。
    // translateY: 向右偏移50。
    if(opType == OP_TYPE_TRANSLATE){
        float translateX = 50;
        float translateY = 50;
        OH_PixelMap_Translate(native, translateX, translateY);
    }

    // 设置顺时针旋转90度。(ok)
    if(opType == OP_TYPE_ROTATE){
        float angle = 90;
        OH_PixelMap_Rotate(native, angle);
    }

    // 设置翻转(ok)
    // flipX: 水平翻转,0为不翻转,1为翻转。
    // flipY: 垂直翻转,0为不翻转,1为翻转。
    if(opType == OP_TYPE_FLIP){
        int32_t flipX = 0;
        int32_t flipY = 1;
        OH_PixelMap_Flip(native, flipX, flipY);
    }

    // 设置裁剪区域。(ok)
    // cropX: 裁剪起始点横坐标。
    // cropY: 裁剪起始点纵坐标。
    // cropH: 裁剪高度10,方向为从上往下(裁剪后的图片高度为10)。
    // cropW: 裁剪宽度10,方向为从左到右(裁剪后的图片宽度为10)。
    if(opType == OP_TYPE_CROP){
        int32_t cropX = 0;
        int32_t cropY = 0;
        int32_t cropW = pixelmapInfo.width/2;
        int32_t cropH = pixelmapInfo.height/2;
        OH_PixelMap_Crop(native, cropX, cropY, cropW, cropH);
    }

    // 获取PixelMap对象数据的内存地址,并锁定该内存
    void *pixelAddr = nullptr;
    OH_PixelMap_AccessPixels(native, &pixelAddr);
    // 释放PixelMap对象数据的内存锁
    OH_PixelMap_UnAccessPixels(native);
    
    //PixelMap进行处理后调用Drawing进行渲染出来
    bool bRender = SampleBitMap::CanvasRender(env, info, SCENE_TYPE_BASE, native);
    if(!bRender){
        return nullptr;
    }
    
    return result;
}
bool SampleBitMap::CanvasRender(napi_env env, napi_callback_info info, int32_t sceneType, NativePixelMap *native)
{
    if ((env == nullptr) || (info == nullptr)) {
        DRAWING_LOGE("NapiDrawPattern: env or info is null");
        return false;
    }

    napi_value thisArg;
    if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
        DRAWING_LOGE("NapiDrawPattern: napi_get_cb_info fail");
        return false;
    }

    napi_value exportInstance;
    if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
        DRAWING_LOGE("NapiDrawPattern: napi_get_named_property fail");
        return false;
    }

    OH_NativeXComponent *nativeXComponent = nullptr;
    if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
        DRAWING_LOGE("NapiDrawPattern: napi_unwrap fail");
        return false;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        DRAWING_LOGE("NapiDrawPattern: Unable to get XComponent id");
        return false;
    }
    DRAWING_LOGI("ID = %{public}s", idStr);
    std::string id(idStr);
    SampleBitMap *render = SampleBitMap().GetInstance(id);
    if (render != nullptr) {
        render->Prepare();
        render->Create();
        render->ConstructPath();
        render->SetPenAndBrush();
        render->DrawPath(sceneType, native);
        render->DisPlay();
        render->Destroy();
        DRAWING_LOGI("DrawPath executed");
    } else {
        DRAWING_LOGE("render is nullptr");
    }
    return true;
}
  • 将PixelMap矩形图片裁剪成圆形

备注:此功能为扩展功能,通过上面接口做不到,PixelMap不提供该功能的API能力支持

Native侧准备:

  1. 调用OH_Drawing_PathCreate方法创建绘制路径cPath。
  2. 调用OH_Drawing_PathAddCircle(cPath_, x, y, radius, PATH_DIRECTION_CCW)方法添加圆形路径。
  3. 调用OH_Drawing_CanvasClipPath (cCanvas_, cPath_ , INTERSECT, true)方法将cavas区域裁剪成指定形状并和目标区域取交集。
  4. 调用Drawing的OH_Drawing_CanvasDrawPixelMapRect方法将PixelMap图形在cavas中渲染出来,即可得到圆形图片。

案例代码

napi_value SampleBitMap::NapiDrawPattern(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    napi_value argValue[1] = {nullptr};
    size_t argCount = 1;
    if (napi_get_cb_info(env, info, &argCount, argValue, &thisVar, nullptr) != napi_ok || argCount < 1 ||
        argValue[0] == nullptr) {
        return nullptr;
    }
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    // 初始化PixelMap对象数据
    NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, argValue[0]);
    if (native == nullptr) {
        return nullptr;
    }
    OH_LOG_INFO(LOG_APP, "初始化PixelMap对象数据成功");
    // PixelMap渲染输出
    bool bRender = SampleBitMap::CanvasRender(env, info, (int32_t)SCENE_TYPE::SCENE_TYPE_EXTEND, native);
    if (!bRender) {
        return nullptr;
    }

    return result;
}
void SampleBitMap::DrawPath(uint32_t type, NativePixelMap *native)
{
    //矩形四个角坐标
    float aX = width_ / 8;
    float aY = height_ * 3 / 8;
    float bX = width_ / 8;
    float bY = height_ * 5 / 8;
    float cX = width_ * 7 / 8;
    float cY = height_ * 5 / 8;
    float dX = width_ * 7 / 8;
    float dY = height_ * 3 / 8;
    cPath_ = OH_Drawing_PathCreate();
    // 闭合形状,path绘制完毕
    OH_Drawing_PathClose(cPath_);
    
    if(native != nullptr){
        OH_Drawing_PixelMap* cPixelMap = OH_Drawing_PixelMapGetFromNativePixelMap(native);
        if(cPixelMap == nullptr){
            return;
        }
        if (type == SCENE_TYPE_BASE) {
            //场景一:Pixmap基础操作
            OH_Drawing_CanvasDrawPixelMapRect (cCanvas_, cPixelMap, nullptr, Rect, nullptr);
        }else if (type == SCENE_TYPE_EXTEND) {
            //场景二:Pixmap扩展操作,将矩形图片裁剪成圆形头像
            //PATH_DIRECTION_CW 顺时针方向添加闭合轮廓  PATH_DIRECTION_CCW    逆时针方向添加闭合轮廓
            OH_Drawing_PathAddCircle(cPath_, (cX-aX)/2+aX, (cY-aY)/2+aY, (cY-aY)/2, PATH_DIRECTION_CCW);
    
            //DIFFERENCE将指定区域裁剪(取差集)。  INTERSECT将指定区域保留(取交集)。 参数4:真为抗锯齿,假则不做抗锯齿处理。
            OH_Drawing_CanvasClipPath (cCanvas_, cPath_ , INTERSECT, true);
    
            //必须放后面
            OH_Drawing_CanvasDrawPixelMapRect (cCanvas_, cPixelMap, nullptr, Rect, nullptr);
        }
    } else {
        //在画布上画path的形状,五角星的边框样式为pen设置,颜色填充为Brush设置
        //OH_Drawing_CanvasDrawPath(cCanvas_, cPath_);
        //绘制圆角矩形
        //OH_Drawing_CanvasDrawRoundRect (cCanvas_ , RoundRect);
    }
}

 

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐