HarmonyOS鸿蒙Next中pixelmap如何处理成圆角?

HarmonyOS鸿蒙Next中pixelmap如何处理成圆角? 【问题描述】

我的需求是想要将pixelmap图片进行圆角的处理,然后显示在鸿蒙的image组件上,注意我的需求是直接处理原图片,而不是单纯的用组件的圆角属性实现的方式。

请问鸿蒙这边有相关的功能接口吗,或者是可以使用的三方库?

【版本信息】不涉及

3 回复

【背景知识】

  • PixelMap图像像素类,用于读取或写入图像数据以及获取图像信息。图片解码是指将所支持格式的图片文件解码成统一的PixelMap格式,目前支持的图片格式有JPEG、PNG、GIF、WebP、BMP、SVG、ICO、DNG、HEIF。PixelMap主要用于图像显示或进一步处理。
  • 通过接口readPixelsToBufferSync按照PixelMap的像素格式,读取PixelMap的图像像素数据,并写入缓冲区中,同步返回结果。对获取的像素数据做相应的处理,达到想要的效果。

【解决方案】

实现图片圆角裁剪,主要是过滤图片四个角的像素点,将圆弧之外的像素点设置为透明即可,具体步骤如下:

  1. 将图片转为PixelMap,读取像素数据。
  2. 过滤图片四个角的像素点,将圆弧之外的像素点设置为透明。
  3. 将处理好的像素数据重新写入图片中。

完整代码示例如下:

import { image } from '@kit.ImageKit';
import { common } from '@kit.AbilityKit';

// 对图片四个角做弧度裁剪,达到圆角效果
function getCirclePixelMap(pixelMap: PixelMap, radius: number) {
  let imageInfo = pixelMap.getImageInfoSync();
  let width = imageInfo.size.width;
  let height = imageInfo.size.height;
  let radius2 = radius * radius; // 圆角弧度半径的平方
  let readBuffer: ArrayBuffer = new ArrayBuffer(width * height * 4);
  pixelMap.readPixelsToBufferSync(readBuffer);
  let uint8BufferTemp: Uint8Array = new Uint8Array(readBuffer); // 像素数据转为Uint8Array
  // 左上角,通过判断当前像素点到坐标(radius, radius)的距离平方是否大于圆角弧度半径的平方
  for (let h = 0; h < radius; h++) {
    for (let w = 0; w < radius; w++) {
      let indexTemp = h * width + w;
      let value = (w - radius) * (w - radius) + (h - radius) * (h - radius);
      if (value > radius2) {
        uint8BufferTemp[indexTemp * 4] = 0;
        uint8BufferTemp[indexTemp * 4 + 1] = 0;
        uint8BufferTemp[indexTemp * 4 + 2] = 0;
        uint8BufferTemp[indexTemp * 4 + 3] = 0;
      }
    }
  }

  // 右上角,通过判断当前像素点到坐标(width-radius, radius)的距离平方是否大于圆角弧度半径的平方
  for (let h = 0; h < radius; h++) {
    for (let w = width - radius; w < width; w++) {
      let indexTemp = h * width + w;
      let value = (radius + w - width) * (radius + w - width) + (h - radius) * (h - radius);
      if (value > radius2) {
        uint8BufferTemp[indexTemp * 4] = 0;
        uint8BufferTemp[indexTemp * 4 + 1] = 0;
        uint8BufferTemp[indexTemp * 4 + 2] = 0;
        uint8BufferTemp[indexTemp * 4 + 3] = 0;
      }
    }
  }

  // 左下角,通过判断当前像素点到坐标(radius, height-radius)的距离平方是否大于圆角弧度半径的平方
  for (let h = height - radius; h < height; h++) {
    for (let w = 0; w < radius; w++) {
      let indexTemp = h * width + w;
      let value = (w - radius) * (w - radius) + (radius + h - height) * (radius + h - height);
      if (value > radius2) {
        uint8BufferTemp[indexTemp * 4] = 0;
        uint8BufferTemp[indexTemp * 4 + 1] = 0;
        uint8BufferTemp[indexTemp * 4 + 2] = 0;
        uint8BufferTemp[indexTemp * 4 + 3] = 0;
      }
    }
  }

  // 右下角,通过判断当前像素点到坐标(width-radius, height-radius)的距离平方是否大于圆角弧度半径的平方
  for (let h = height - radius; h < height; h++) {
    for (let w = width - radius; w < width; w++) {
      let indexTemp = h * width + w;
      let value = (radius + w - width) * (radius + w - width) + (radius + h - height) * (radius + h - height);
      if (value > radius2) {
        uint8BufferTemp[indexTemp * 4] = 0;
        uint8BufferTemp[indexTemp * 4 + 1] = 0;
        uint8BufferTemp[indexTemp * 4 + 2] = 0;
        uint8BufferTemp[indexTemp * 4 + 3] = 0;
      }
    }
  }

  pixelMap.writeBufferToPixelsSync(uint8BufferTemp.buffer);
  return pixelMap;
}

function getPixelMapFromMedia(context: Context, fileNameId: number) {
  const resourceMgr = context.resourceManager;
  let mediaFileBuffer = resourceMgr.getMediaContentSync(fileNameId);
  const imageSource: image.ImageSource = image.createImageSource(mediaFileBuffer.buffer);
  let decodingOptions: image.DecodingOptions = {
    editable: true,
    desiredPixelFormat: 3,
  };
  const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
  return pixelMap;
}

@Entry
@Component
struct Index {
  @State pixelMap: image.PixelMap | undefined = undefined;
  @State pixelMapTemp: image.PixelMap | undefined = undefined;
  private context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;

  aboutToAppear() {
    this.pixelMap = getPixelMapFromMedia(this.context, $r('app.media.background').id); // background.png为测试图片,开发者需要在media目录下替换为实际图片。
  }

  build() {
    Column({ space: 5 }) {
      Column() {
        Image(this.pixelMap)
          .objectFit(ImageFit.Contain)
          .margin({ right: 5 })
          .width('90%')
          .height(240)
        Image(this.pixelMapTemp)
          .objectFit(ImageFit.Contain)
          .margin({ top: 10 })
          .width('90%')
          .height(240)
      }

      Button('裁剪圆角')
        .backgroundColor('#0D9FFB')
        .width(120)
        .height(40)
        .margin({ top: 5, left: 5, right: 5 })
        .onClick(() => {
          this.pixelMapTemp = getCirclePixelMap(this.pixelMap as PixelMap, 100);
        })
    }
    .height('100%')
    .width('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中pixelmap如何处理成圆角?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,使用Image组件的clip属性设置圆角。通过clipCircleRoundRect参数定义形状,例如clip(new Circle({ width: 100, height: 100 }))创建圆形,或clip(new RoundRect({ width: 100, height: 100, radius: 10 }))实现圆角矩形。直接作用于PixelMap数据,无需转换。

在HarmonyOS Next中,可以通过ImagePackerPixelMap结合Canvas来实现对原始PixelMap的圆角处理。具体步骤如下:

  1. 创建圆角遮罩:使用Canvas绘制一个圆角矩形路径,作为裁剪区域。
  2. 绘制图像:将原始PixelMap绘制到Canvas上,并应用圆角路径的裁剪。
  3. 导出处理后的PixelMap:通过ImagePacker将Canvas内容重新打包为圆角PixelMap。

示例代码:

import image from '@ohos.multimedia.image';
import display from '@ohos.display';

// 假设已有原始pixelMap
let originalPixelMap: image.PixelMap;

// 创建Canvas
let radius = 20; // 圆角半径
let imageWidth = originalPixelMap.getImageInfo().size.width;
let imageHeight = originalPixelMap.getImageInfo().size.height;

// 通过OffscreenCanvas创建绘制环境
let offscreenCanvas = new OffscreenCanvas(imageWidth, imageHeight);
let ctx = offscreenCanvas.getContext('2d');

// 绘制圆角路径
ctx.beginPath();
ctx.moveTo(radius, 0);
ctx.arcTo(imageWidth, 0, imageWidth, imageHeight, radius);
ctx.arcTo(imageWidth, imageHeight, 0, imageHeight, radius);
ctx.arcTo(0, imageHeight, 0, 0, radius);
ctx.arcTo(0, 0, imageWidth, 0, radius);
ctx.closePath();
ctx.clip();

// 绘制原始图像
ctx.drawImage(originalPixelMap, 0, 0, imageWidth, imageHeight);

// 获取处理后的PixelMap
let roundedPixelMap = offscreenCanvas.transferToImage().createPixelMap();

完成后,可将roundedPixelMap设置到Image组件显示。此方法直接修改了图像数据,满足处理原图的需求。

回到顶部