基于Native对PixelMap进行图形处理
·
场景描述
部分用户需要在Native侧实现对PixelMap进行图形处理。因此本文详细讲述了如何在Native侧操作PixelMap,包含对图片的旋转裁剪等。
方案描述
- 在ArkTS侧:
- 在Index.ets文件中添加XComponent组件,使用XComponent组件来送显图片。
- 通过资源管理器获取图片并解码成PixelMap对象。
- 在Native侧使用c++对PixelMap进行数据处理:
- 创建PluginManager单例来管理XComponent组件。
- 创建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图片的基础功能
- TS侧准备。
- 在aboutToAppear方法中创建PixelMap对象。
- 调用c++的transform接口,传入PixelMap对象。
- Native侧准备。
- 接收数据后调用OH_PixelMap_Rotate和OH_PixelMap_Crop等方法对图片进行处理。
- 调用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 < 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侧准备:
- 调用OH_Drawing_PathCreate方法创建绘制路径cPath。
- 调用OH_Drawing_PathAddCircle(cPath_, x, y, radius, PATH_DIRECTION_CCW)方法添加圆形路径。
- 调用OH_Drawing_CanvasClipPath (cCanvas_, cPath_ , INTERSECT, true)方法将cavas区域裁剪成指定形状并和目标区域取交集。
- 调用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);
}
}
更多推荐
所有评论(0)