目录

如何实现将图片分割成九宫格并保存至图库

问题现象

在一些场景中需要将图片分割成九宫格之类的图片再发布,以达到增强视觉效果以及互动性和趣味性,如何实现选取的图片分割成九宫格呢?

背景知识

解决方案

实现以上功能,具体分三步:

  1. 通过photoAccessHelper访问图库中的图片(限制单张图片)。
  2. 使用image图片处理能力,创建图片源、PixelMap位图后根据尺寸裁剪图片为九宫格。
  3. 将图片重新编码后再使用SaveButton安全控件保存每张图片至图库。

完整示例代码如下:

import fs from '@ohos.file.fs';
import image from '@ohos.multimedia.image';
import { common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';


interface puzzleItem {
  // 拼图块的像素地图
  pixelMap: image.PixelMap;


  // 索引位置
  index: number;
}


@Entry
@Component
struct Index {
  @State imgUri: string = '';
  @State puzzleItems: Array<puzzleItem> = [];
  context = this.getUIContext().getHostContext() as common.UIAbilityContext;


  // 弹出图片选择器方法
  async selectImg() {
    try {
      // 设置图片选择器选项
      const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE。
      photoSelectOptions.maxSelectNumber = 1; // 选择媒体文件的最大数目。
      let uris: Array<string> = [];
      const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
      photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
        uris = photoSelectResult.photoUris;
        let file1 = fs.openSync(uris[0]);
        const dateStr = (new Date().getTime()).toString();
        // 临时文件目录
        let newPath = this.context.cacheDir + `/${dateStr + file1.name}`;
        fs.copyFileSync(file1.fd, newPath);
        let file2 = fs.openSync(newPath, fs.OpenMode.READ_WRITE);
        // 更新状态变量:设置显示图片的URI
        this.imgUri = file2.path;
        fs.closeSync(file1.fd);
        fs.closeSync(file2.fd);
        // 图片分割九宫格
        this.imgSplit();
      }).catch((error: BusinessError) => {
        console.error(`errCode: ${error.code}, errMessage: ${error.message}`);
      });
    } catch (e) {
    }
  }


  // 图片分割九宫格
  async imgSplit() {
    try {
      // 创建图片源对象
      const imageSource: image.ImageSource = image.createImageSource(this.imgUri);
      // 图片解码选项
      let decodingOptions: image.DecodingOptions = {
        editable: true,
      };
      // 创建像素地图
      let pixelMap: image.PixelMap = await imageSource.createPixelMap(decodingOptions);
      // 获取图片信息
      let imageInfo: image.ImageInfo = await pixelMap.getImageInfo();


      // 计算每个拼图块的大小
      const puzzleSize: image.Size = {
        width: imageInfo.size.width / 3,
        height: imageInfo.size.height / 3,
      };


      // 清空已有拼图块数据
      this.puzzleItems.splice(0);


      // 遍历图片生成9个拼图块
      let count = 0;
      for (let row = 0; row < 3; row++) {
        for (let col = 0; col < 3; col++) {
          const newImageSource = image.createImageSource(this.imgUri);
          let newPixelMap = await newImageSource.createPixelMap(decodingOptions);
          // 计算裁剪区域
          const puzzleRect: image.Region = {
            x: col * puzzleSize.width,
            y: row * puzzleSize.height,
            size: puzzleSize,
          };
          // 裁剪像素地图
          await newPixelMap.crop(puzzleRect);
          // 创建并添加拼图块至数组
          const item: puzzleItem = {
            pixelMap: newPixelMap,
            index: count++,
          };
          this.puzzleItems.push(item);
        }
      }
    } catch (e) {
    }
  }


  // 通过SaveButton保存图片至图库
  handleSaveButtonClick: SaveButtonCallback =
    async (event: ClickEvent, result: SaveButtonOnClickResult, error?: BusinessError) => {
      let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
      let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;
      let extension: string = 'png';
      if (result === SaveButtonOnClickResult.SUCCESS) {
        this.puzzleItems.forEach(async (item: puzzleItem) => {
          try {
            let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
              photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoType, extension);
            let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);


            let packOpts: image.PackingOption = { format: 'image/png', quality: 98 };
            const imagePackerObj: image.ImagePacker = image.createImagePacker();
            const buffer = await imagePackerObj.packToData(item.pixelMap, packOpts);


            assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, buffer);
            await phAccessHelper.applyChanges(assetChangeRequest);
          } catch (error) {
            console.error(`errCode: ${error.code}, errMessage: ${error.message}`);
          }
        });
      } else {
        console.error(`errCode: ${error?.code}, errMessage: ${error?.message}`);
      }
    };


  build() {
    Column() {
      Button('选择图片')
        .margin({ top: 20, bottom: 10 })
        .onClick(() => {
          this.selectImg();
        });
      SaveButton({
        text: SaveDescription.SAVE,
        buttonType: ButtonType.Capsule
      })
        .onClick(this.handleSaveButtonClick);


      Column() {
        Text('九宫格图片').margin({ top: 10 });
        Grid() {
          ForEach(this.puzzleItems, (item: puzzleItem) => {
            GridItem() {
              Image(item.pixelMap)
                .autoResize(true)
                .syncLoad(true)
                .width('200lpx')
                .height('200lpx')
                .margin('5lpx');
            };
          });
        };
      }
      .padding({ top: 10, bottom: 10 });


    }
    .width('100%');
  }
}