如何在鸿蒙系统中对相册中的图片或视频进行编辑?

我的应用需要允许用户对相册中的图片或视频进行编辑,比如裁剪、旋转或添加滤镜。请问,在鸿蒙系统中,如何使用相册管理模块(photoAccessHelper)来实现对图片或视频的编辑功能?
需要哪些API调用和权限?能否提供一个示例代码,展示如何对相册中的资源进行编辑?

本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

阅读 1.8k
avatarAI BotBETA
### 回答

在鸿蒙系统中对相册中的图片或视频进行编辑,通常需要使用鸿蒙系统提供的多媒体处理API以及相册管理模块(photoAccessHelper)。以下是一些关键步骤和示例代码,帮助你实现对图片或视频的编辑功能。

#### 所需权限

首先,你需要在`config.json`文件中声明必要的权限,以便应用能够访问相册和进行多媒体处理。例如:

"module": {

"package": "com.yourapp.package",
"reqPermissions": [
    "ohos.permission.READ_MEDIA",
    "ohos.permission.WRITE_MEDIA",
    "ohos.permission.INTERNET" // 如果需要下载滤镜等资源
]

}


#### 所需的API调用

1. **相册管理模块(photoAccessHelper)**:用于访问相册中的图片和视频。
2. **多媒体处理API**:用于对图片和视频进行裁剪、旋转和添加滤镜等操作。

#### 示例代码

以下是一个简化的示例代码,展示了如何使用photoAccessHelper选择图片,并使用多媒体处理API进行简单的编辑(如裁剪和旋转):

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.media.common.MediaEditOption;
import ohos.media.common.MediaEditor;
import ohos.media.common.MediaPicker;
import ohos.media.image.ImageEditor;
import ohos.multimodalinput.event.TouchEvent;
import ohos.security.SystemPermission;

public class MainAbility extends Ability {

private static final int PICK_IMAGE_REQUEST = 1;

@Override
protected void onStart(Intent intent) {
    super.onStart(intent);
    setUIContent(ResourceTable.Layout_ability_main);

    // 请求必要的权限
    verifyPermissions();

    // 设置点击事件来选择图片
    Component pickImageButton = findComponentById(ResourceTable.Id_pick_image_button);
    pickImageButton.setClickedListener(new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            pickImageFromGallery();
        }
    });
}

private void verifyPermissions() {
    if (!verifySelfPermission(SystemPermission.READ_MEDIA) ||
        !verifySelfPermission(SystemPermission.WRITE_MEDIA)) {
        requestPermissionsFromUser(
            new String[]{SystemPermission.READ_MEDIA, SystemPermission.WRITE_MEDIA},
            0
        );
    }
}

private void pickImageFromGallery() {
    Intent pickImageIntent = new Intent(MediaPicker.ACTION_PICK_IMAGE);
    startAbilityForResult(pickImageIntent, PICK_IMAGE_REQUEST);
}

@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
    super.onAbilityResult(requestCode, resultCode, resultData);
    if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK) {
        String selectedImageUri = resultData.getStringParam(MediaPicker.RESULT_DATA_URI);
        if (selectedImageUri != null) {
            editImage(selectedImageUri);
        }
    }
}

private void editImage(String imageUri) {
    // 创建ImageEditor对象
    ImageEditor imageEditor = new ImageEditor(this);

    // 设置编辑选项,例如裁剪和旋转
    MediaEditOption editOption = new MediaEditOption.Builder()
        .setCropArea(100, 100, 300, 300) // 设置裁剪区域(x, y, width, height)
        .setRotation(90) // 设置旋转角度
        .build();

    // 进行编辑
    imageEditor.edit(imageUri, editOption, new MediaEditor.OnEditCompletedListener() {
        @Override
        public void onSuccess(String outputUri) {
            // 编辑成功,outputUri是编辑后的图片路径
            // 可以在这里进行进一步处理,如显示或保存
        }

        @Override
        public void onFailed(int errorCode, String errorMessage) {
            // 编辑失败,处理错误
        }
    });
}

}


#### 解释

1. **权限请求**:在`onStart`方法中调用`verifyPermissions`来检查并请求必要的权限。
2. **选择图片**:使用`MediaPicker`的`ACTION_PICK_IMAGE`动作来从相册中选择图片。
3. **编辑图片**:使用`ImageEditor`类进行编辑操作,如裁剪和旋转。编辑完成后,通过`OnEditCompletedListener`回调来处理成功或失败的结果。

注意:以上代码是简化的示例,实际应用中可能需要处理更多的细节,如错误处理、UI更新等。此外,对于视频编辑,你可能需要使用更复杂的API和编辑选项。
1 个回答

参考方案如下:
1)在arkts侧解码resources下的图片,获取image.pixelMap,并设置图片滤镜亮度、对比度、裁剪等参数,调用napi接口将image.PixelMap和滤镜参数传递到native侧
2)native侧将获取到的image.PixelMap转换成可作为ImageEffect输入的OH_PixelmapNative对象
3)将OH_PixelmapNative作为ImageEffect的输入,根据传入的滤镜参数,以此往滤镜链路里添加亮度、对比度、裁剪滤镜以及自定义滤镜
4)将添加滤镜效果的OH_PixelmapNative对象,转化成image.PixelMap对象返回给arkts层回显
主要代码参考如下:
1)构建传递给native侧的滤镜参数

private confirmInfo() {
    this.filterOptions = [];
    if (this.brightnessSelect) {
      let brightnessArray: (string | number)[] = [];
      brightnessArray.push("Brightness", this.brightnessSetValue);
      this.filterOptions.push(brightnessArray);
    }
    if (this.contrastSelect) {
      let contrastArray: (string | number)[] = [];
      contrastArray.push("Contrast", this.contrastSetValue);
      this.filterOptions.push(contrastArray);
    }
    if (this.cropSelect) {
      let cropArray: (string | number)[] = [];
      cropArray.push("Crop", this.cropSetValue);
      this.filterOptions.push(cropArray);
    }
    if (this.customSelect) {
      let customArray: (string | number)[] = [];
      customArray.push("CustomBrightness", this.customSetValue);
      this.filterOptions.push(customArray);
    }
}

2)解码resources下的图片,获取image.pixelMap,并设置图片滤镜亮度、对比度、裁剪等参数,调用napi接口将image.PixelMap和滤镜参数传递到native侧

if (this.brightnessSelect || this.contrastSelect || this.cropSelect || this.customSelect) {
    let pixelMap = await ImageUtils.getInstance().getPixelMap($r('app.media.ic_1080x1920'));
    // 调用napi接口,传入image.pixelMap对象及滤镜参数
    this.displayPixelMap = imageEffect.apply(pixelMap, [...this.filterOptions]);
 }
 
async getPixelMap(resource: Resource): Promise<image.PixelMap> {
   const resourceStr = getContext(this).resourceManager;
   let imageBuffer = await resourceStr.getMediaContent(resource);
   return  await image.createImageSource(imageBuffer.buffer as object as ArrayBuffer).createPixelMap();
}

3)native侧将image.PixelMap转化成OH_PixelmapNative

OH_PixelmapNative* pixelmapNativePtr = nullptr;
OH_PixelmapNative_ConvertPixelmapNativeFromNapi(env, args[EXPECTED_ARGS_ZERO],&pixelmapNativePtr);

4)根据napi接口接收到的滤镜参数,构建滤镜链路

std::vector<std::vector<FilterArrayData>> GetFilters(napi_env env, napi_value arg)
{
    std::vector<std::vector<FilterArrayData>> data;
    napi_status status;
 
    bool is_array;
    status = napi_is_array(env, arg, &is_array);
    CHECK_AND_RETURN_RET_LOG(is_array == true, data, "GetFilters napi_is_array fail! status=%{public}d", status);
 
    // Handle napi array length
    auto array_length = GetNapiArrayLength(env, arg);
    CHECK_AND_RETURN_RET_LOG(array_length.first == napi_ok, data,
        "GetFilters napi_get_array_length fail! status=%{public}d", array_length.first);
 
    for (uint32_t i = 0; i < array_length.second; i++) {
        napi_value element;
        status = napi_get_element(env, arg, i, &element);
        CHECK_AND_RETURN_RET_LOG(status == napi_ok, data, "GetFilters napi_get_element fail! status=%{public}d",
            status);
 
        auto child_length = GetNapiArrayLength(env, element);
        CHECK_AND_RETURN_RET_LOG(child_length.first == napi_ok, data,
            "GetFilters child napi_get_array_length fail! status=%{public}d", child_length.first);
 
        std::vector<FilterArrayData> row;
        FilterArrayData filterArrayData;
        for (uint32_t j = 0; j < child_length.second; j++) {
            napi_value childElement;
            status = napi_get_element(env, element, j, &childElement);
 
            napi_valuetype valueType;
            status = napi_typeof(env, childElement, &valueType);
            CHECK_AND_RETURN_RET_LOG(status == napi_ok, data,
                "GetFilters child napi_typeof fail! status=%{public}d, value=%{public}d", status, valueType);
 
            if (valueType == napi_string) {
                filterArrayData.name = HandleStringType(env, childElement, status);
            } else if (valueType == napi_number) {
                filterArrayData.value = HandleNumberType(env, childElement, status);
            }
        }
        row.push_back(filterArrayData);
        data.push_back(row);
    }
 
    return data;
}

5)构建并注册自定义滤镜

static ImageEffect_FilterDelegate delegate;
napi_value ImageEdit::RegisterCustomBrightness()
{
    napi_value result = nullptr;
    // 自定义算子能力信息
    OH_EffectFilterInfo *effectInfo = OH_EffectFilterInfo_Create();
    OH_EffectFilterInfo_SetFilterName(effectInfo, "CustomBrightness");
    ImageEffect_BufferType bufferType = ImageEffect_BufferType::EFFECT_BUFFER_TYPE_PIXEL;
    OH_EffectFilterInfo_SetSupportedBufferTypes(effectInfo, 1, &bufferType);
    ImageEffect_Format format = ImageEffect_Format::EFFECT_PIXEL_FORMAT_RGBA8888;
    OH_EffectFilterInfo_SetSupportedFormats(effectInfo, 1, &format);
    // 自定义算子实现接口
    delegate = {
        .setValue = [](OH_EffectFilter *filter, const char *key, const ImageEffect_Any *value) { return true; },
        .render = [](OH_EffectFilter *filter, OH_EffectBufferInfo *src,
            OH_EffectFilterDelegate_PushData pushData) { 
            return true; 
         },
        .save = [](OH_EffectFilter *filter, char **info) { 
            return true; 
        },
        .restore = [](const char *info) { 
            return OH_EffectFilter_Create("CustomBrightness"); 
        }
    };
// 注册自定义滤镜
    ImageEffect_ErrorCode errorCode = OH_EffectFilter_Register(effectInfo, &delegate);
    CHECK_AND_RETURN_RET_LOG(errorCode == ImageEffect_ErrorCode::EFFECT_SUCCESS, result,
        "OH_EffectFilter_Register fail! errorCode = %{public}d", errorCode);
    return result;
}
6)将OH_PixelmapNative作为ImageEffect的输入,根据传入的滤镜参数,以此往滤镜链路里添加亮度、对比度、裁剪滤镜以及自定义滤镜

// 创建ImageEffect对象
OH_ImageEffect *imageEffect = OH_ImageEffect_Create("imageEdit");
CHECK_AND_RETURN_RET_LOG(imageEffect != nullptr, result, "OH_ImageEffect_Create fail!");
std::shared_ptr<OH_ImageEffect> imageEffectPtr(imageEffect, [](OH_ImageEffect *imageEffect) {
    OH_ImageEffect_Release(imageEffect);
});
// 将步骤4中构建的滤镜链路添加到ImageEffect对象中
for (int i = 0; i < filters.size(); i++) {
    OH_EffectFilter *filter = AddFilter(imageEffectPtr.get(), filters[i][0].name.c_str());
    CHECK_AND_RETURN_RET_LOG(filter != nullptr, result, "OH_ImageEffect_AddFilter fail!");
    SetFilterValue(filter, filters[i][0].name.c_str(), filters[i][0].value, pixelmapNativePtr);
}
// 将由image.PixelMap转化得到的oh_PixelmapNative设置为ImageEffect的输入
ImageEffect_ErrorCode errorCode = OH_ImageEffect_SetInputPixelmap(imageEffectPtr.get(), pixelmapNativePtr);
CHECK_AND_RETURN_RET_LOG(errorCode == ImageEffect_ErrorCode::EFFECT_SUCCESS, result,
    "OH_ImageEffect_SetInputPixelMap fail! errorCode = %{public}d", errorCode);
// 开启滤镜效果
errorCode = OH_ImageEffect_Start(imageEffectPtr.get());
CHECK_AND_RETURN_RET_LOG(errorCode == ImageEffect_ErrorCode::EFFECT_SUCCESS, result,
    "OH_ImageEffect_Start fail! errorCode = %{public}d", errorCode);

6)将添加滤镜效果的OH_PixelmapNative对象,转化成image.PixelMap对象返回给arkts层回显

OH_PixelmapNative_ConvertPixelmapNativeToNapi(env, pixelmapNativePtr, &result);
return result;
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进