实现图片部分区域透明化
===========

#### 问题现象

在图片处理时，如何实现图片部分区域自定义。例如使部分区域透明化，以达到图片部分显示的效果。

#### 背景知识

*   [PixelMap](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap)图像像素类，用于读取或写入图像数据以及获取图像信息。图片解码是指将所支持格式的图片文件解码成统一的PixelMap格式，目前支持的图片格式有JPEG、PNG、GIF、WebP、BMP、SVG、ICO、DNG、HEIF。PixelMap主要用于图像显示或进一步处理。
*   通过接口[readPixelsToBufferSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap#readpixelstobuffersync12)按照PixelMap的像素格式，读取PixelMap的图像像素数据，并写入缓冲区中，同步返回结果。对获取的像素数据做相应的处理，达到想要的效果。

#### 解决方案

实现图片部分区域透明化，可参考以下步骤：

1.  使用图片解码获取需要转换的图片像素数据，生成后续操作中使用的PixelMap对象，可参考[使用ImageSource完成图片解码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/image-decoding)。
2.  使用[getImageInfo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap#getimageinfo7-1)获取图片宽高信息，为后续区域指定提供参考标准。
3.  使用readPixelsToBufferSync将图片像素数据写入缓冲区中，通过像素数组遍历，将符合条件的像素值进行替换。
4.  使用[createPixelMapSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-imagesource#createpixelmapsync12)将修改后的像素数据写入新的PixelMap图像中，并通过Image组件进行展示。

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

_//_ _透明化方向枚举_
enum TransparentDirection {
  Left,
  Right,
  Top,
  Bottom,
}

_/\*\*_
 _\* 将图片按方向做部分透明处理_
 _\* @param pixelMap：需要透明化处理的位图_
 _\* @param type：透明化的方向_
 _\* @param transparentCount：透明化的比例_
 _\* @returns 处理好的图片_
 _\*/_
function getTransparentPixelMap(pixelMap: PixelMap, type: TransparentDirection,
  transparentCount: number): image.PixelMap {
  let imageInfo \= pixelMap.getImageInfoSync();
  let width \= imageInfo.size.width;
  let height \= imageInfo.size.height;
 _ // readBuffer：保存原始位图数据_
  _// desBuffer：保存透明化处理后的位图数据_
  let readBuffer: ArrayBuffer \= new ArrayBuffer(width \* height \* 4);
  let desBuffer: ArrayBuffer \= new ArrayBuffer(width \* height \* 4);
 _ // 读取原始位图数据_
  pixelMap.readPixelsToBufferSync(readBuffer);
  _// uint32Buffer：原始位图数据，4个字节为一个像素_
  let uint32Buffer: Uint32Array \= new Uint32Array(readBuffer);
  let uint8BufferTemp: Uint8Array \= new Uint8Array(desBuffer);
  _// 透明化处理_
  for (let h \= 0; h < height; h++) {
    for (let w \= 0; w < width; w++) {
      let index \= h \* width \+ w;
      let indexTemp \= h \* width \+ w;
      let pixelColor \= uint32Buffer\[index\];
     _ // 遍历所有像素点，判断每个像素点是否需要透明化处理。_
      let isTransparent \= false;
      if (type \== TransparentDirection.Left) {
        if (w < transparentCount \* width) {
          isTransparent \= true;
        }
      }
      if (type \== TransparentDirection.Right) {
        if (w \>\= transparentCount \* width) {
          isTransparent \= true;
        }
      }
      if (type \== TransparentDirection.Top) {
        if (h < transparentCount \* height) {
          isTransparent \= true;
        }
      }
      if (type \== TransparentDirection.Bottom) {
        if (h \>\= transparentCount \* height) {
          isTransparent \= true;
        }
      }
     _ // 需要透明化处理的，Alpha值设置为0。不需要透明化处理的，复制原始像素值。_
      if (isTransparent \== true) {
        uint8BufferTemp\[indexTemp \* 4\] \= 255;
        uint8BufferTemp\[indexTemp \* 4 \+ 1\] \= 255;
        uint8BufferTemp\[indexTemp \* 4 \+ 2\] \= 255;
        uint8BufferTemp\[indexTemp \* 4 \+ 3\] \= 0;
      } else {
        uint8BufferTemp\[indexTemp \* 4\] \= (pixelColor \>\> 16) & 0x000000ff;
        uint8BufferTemp\[indexTemp \* 4 \+ 1\] \= (pixelColor \>\> 8) & 0x000000ff;
        uint8BufferTemp\[indexTemp \* 4 \+ 2\] \= (pixelColor \>\> 0) & 0x000000ff;
        uint8BufferTemp\[indexTemp \* 4 \+ 3\] \= (pixelColor \>\> 24) & 0x000000ff;
      }
    }
  }
  _// 创建透明化处理后的PixelMap对象_
  let color: ArrayBuffer \= uint8BufferTemp.buffer as ArrayBuffer;
  let opts: image.InitializationOptions \=
    { editable: true, pixelFormat: imageInfo.pixelFormat, size: { height: height, width: width } };
  let pixelMapTemp: image.PixelMap \= image.createPixelMapSync(color, opts);
  return pixelMapTemp;
}

@Entry
@Component
struct ImageProcessingPage {
  @State pixelMap: image.PixelMap | undefined \= undefined;
  @State pixelMapTemp: image.PixelMap | undefined \= undefined;
  @State pixelMapTemp1: image.PixelMap | undefined \= undefined;

  getPixelMapByImage(resId: number) {
    let context \= this.getUIContext().getHostContext() as common.UIAbilityContext;
 _   // 获取resourceManager资源管理器_
    const resourceMgr: resourceManager.ResourceManager \= context.resourceManager;
   _ // 获取图片资源文件数据_
    let fileUint8Arr \= resourceMgr.getMediaContentSync(resId);
   _ // 通过图片资源文件的描述符创建ImageSource_
    const imageSource: image.ImageSource \= image.createImageSource(fileUint8Arr.buffer);
    let decodingOptions: image.DecodingOptions \= {
      editable: true,
      desiredPixelFormat: image.PixelMapFormat.RGBA\_8888,
    };
  _  // 解码图片得到对应的PixelMap。_
    const pixelMap: image.PixelMap \= imageSource.createPixelMapSync(decodingOptions);
    return pixelMap;
  }

  aboutToAppear() {
  _  // startIcon仅作展示使用，开发者可以自行调整。_
    this.pixelMap \= this.getPixelMapByImage($r('app.media.startIcon').id);
  }

  @Styles
  ButtonStyle(){
    .backgroundColor('#0D9FFB')
    .width(120)
    .height(35)
    .margin({
      top: 5,
      bottom: 5,
      left: 5,
      right: 5
    });
  }

  build() {
    Column({ space: 5 }) {
      Image(this.pixelMap)
        .objectFit(ImageFit.Contain)
        .margin({ right: 5 })
        .width('45%')
        .height(200)
      Row() {
        Image(this.pixelMapTemp)
          .objectFit(ImageFit.Contain)
          .backgroundColor(Color.Grey)
          .margin({ right: 5 })
          .width('45%')
          .height(200)
        Image(this.pixelMapTemp1)
          .objectFit(ImageFit.Contain)
          .backgroundColor(Color.Grey)
          .width('45%')
          .height(200)
      }

      Text('图片透明处理')
      Row() {
        Button('右边透明50%').ButtonStyle()
          .onClick(() \=\> {
            this.pixelMapTemp \= getTransparentPixelMap(this.pixelMap as image.PixelMap, TransparentDirection.Right, 0.5);
          })
        Button('下边透明50%').ButtonStyle()
          .onClick(() \=\> {
            this.pixelMapTemp1 \=
              getTransparentPixelMap(this.pixelMap as image.PixelMap, TransparentDirection.Bottom, 0.5);
          })
      }
    }
    .justifyContent(FlexAlign.Start)
    .width('100%')
  }
}

#### 总结

实现图片部分区域透明化，可以通过图片编解码修改指定区域像素值后，生成新的PixelMap用于图片展示，以达到部分图片显示的效果。除了实现部分区域透明化，也可以根据场景需求，将图片部分区域转换为指定颜色。