# 使用PixelMap完成图像变换及常见问题

#### 问题现象

如何对图片进行裁剪、缩放、偏移、旋转、翻转、透明度修改等图像变换操作。

#### 背景知识

PixelMap是图像解码后的一种无压缩位图格式，图片解码是指将所支持格式的图片文件解码成统一的PixelMap格式，目前支持的图片格式有JPEG、PNG、GIF、WebP、BMP、SVG、ICO、DNG、HEIF。PixelMap主要用于图像显示或进一步处理。这种格式可以有效地存储图像的原始数据，使其可以方便地进行图像变换，如裁剪、缩放、偏移、旋转、翻转、设置透明度等。

#### 解决方案

使用HarmonyOS的PixelMap进行图像变换主要涉及以下几个步骤：

1.  图片解码：首先，需要加载并解码图片文件，这将返回一个PixelMap对象，该对象用于后续的图像操作。
2.  获取图片信息：在进行变换之前，若需要获取图片的一些基本信息如宽度和高度，可以通过调用异步方法getImageInfo()或同步方法getImageInfoSync()来进行获取。
3.  执行图像变换：包括裁剪、缩放、旋转、翻转、透明度修改等操作，以图片缩放的具体示例代码如下：
    
    ```
    import { image } from '@kit.ImageKit';
    import { common } from '@kit.AbilityKit';
    
    @Entry
    @Component
    struct Index {
      @State imagePixelMap: PixelMap | undefined = undefined;
      @State scaleCount: number = 1;
    
      async aboutToAppear() {
        let pixelMap = this.getPixelMap();
        this.imagePixelMap = pixelMap;
      }
    
      // 获取pixelMap
      getPixelMap() {
        let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // 获取resourceManager资源管理
        const resourceManager = context.resourceManager; // 获取图片数据
        const fileData = resourceManager.getMediaContentSync($r('app.media.startIcon').id); // startIcon为测试图片，开发者需要替换为实际图片
        const buffer = fileData.buffer;
        const imageSource = image.createImageSource(buffer);
        const pixelMap = imageSource.createPixelMapSync({
          editable: true,
          desiredDynamicRange: image.DecodingDynamicRange.AUTO
        });
        return pixelMap;
      }
    
      // 对pixelMap进行缩放
      scalePixelMap(pixelMap: PixelMap, scaleCount: number) {
        pixelMap.scaleSync(scaleCount, scaleCount);
        return pixelMap;
      }
    
      build() {
        Column() {
          Image(this.imagePixelMap ? this.imagePixelMap : '')
            .width('80%')
            .height('60%')
            .objectFit(ImageFit.None)
          Row() {
            Button('缩小')
              .onClick(() => {
                if (this.imagePixelMap) {
                  this.scaleCount = this.scaleCount - 0.1;
                  // 获取图片基本信息，如果宽高大于300，继续缩小
                  let imageInfo = this.imagePixelMap.getImageInfoSync();
                  if (imageInfo.size.width > 300 || imageInfo.size.width > 300) {
                    this.imagePixelMap = this.scalePixelMap(this.imagePixelMap, this.scaleCount);
                  }
                }
              })
          }
        }
        .width("100%")
        .height("100%")
      }
    }
    ```
    

更多图像变换示例效果。

#### 常见FAQ

Q：获取网络图片的PixelMap，如何进行居中剪裁（center crop）？

A：获取到PixelMap后，可调用crop(region: Region)方法，设置剪裁目标区域region参数为居中，对图片进行裁剪。

Q：HarmonyOS系统上，有时候竖屏图片加载时会显示成横屏。

A：有些设备会给图片加一个旋转属性，HarmonyOS相册组件是Image的API无法读取此属性，想要规避这个问题可以通过getImageProperty()获取旋转信息，判断图片是否要旋转，如果需要旋转可以通过rotate(90)进行调整。

Q：图片缩放算法函数scaleSync(x:number,y:number)，x和y大概0.2左右（图片长宽尺寸缩小0.046），最终图片byte值为原始图的0.0558倍，这个结果不符合预期，预期也是0.046倍。

A：scaleSync影响的是PixelMap的比例大小，缩放后编码的数据会变小，但是不一定成比例。

Q：使用PixelMap.rotate旋转图片没有改变。

A：这个问题涉及PixelMap的深拷贝，可以参考深拷贝demo。

Q：相同代码真机中无法正常旋转图片。

A：需要把图片解码设置选项DecodingOptions中设置editable：true。

Q：使用pixelMap.scale缩放图片没有效果。

A：pixelMap.scale是异步函数，需要在执行成功后再进行别的操作，否则图片没有缩放效果，需要使用同步接口pixelMap.scaleSync，或者在接口pixelMap.scale前增加异步await。

Q：如何对拍照后获取的JPEG图像进行裁剪，再写回文件？

A：先将JPEG图像解码获取PixelMap对象，对PixelMap使用crop接口进行裁剪，再将裁剪后的PixelMap编码成JPEG图像重新写入文件即可。

Q：为什么通过resourceManager.getMediaContent方法获取媒体文件内容后，通过createPixelMap方法转换失败？

A：image.createPixelMap方法的入参为图像像素数据，而resourceManager.getMediaContent方法回调函数中返回的是媒体文件内容数据，媒体文件数据可以直接用于播放，但是不能作为image.createPixelMap方法的入参，需要通过解码转换为可用图片像素数据才可以用于图片处理或渲染。