【入门到精通】鸿蒙next开发:基于Native对PixelMap进行图形处理实战

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)


基于Native对PixelMap进行图形处理实战

场景描述

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

方案描述

1、在ArkTS侧:

a. 在Index.ets文件中添加XComponent组件,使用XComponent组件来送显图片。

b. 通过资源管理器获取图片并解码成PixelMap对象。

2、在Native侧使用c++对PixelMap进行数据处理:

a. 创建PluginManager单例来管理XComponent组件。

b. 创建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侧准备。

a. 在aboutToAppear方法中创建PixelMap对象。

b. 调用c++的transform接口,传入PixelMap对象。

2、Native侧准备。

a. 接收数据后调用OH_PixelMap_Rotate和OH_PixelMap_Crop等方法对图片进行处理。

b. 调用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); 
    } 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值