HarmonyOS 多张图片拼接为一张

需求是签字版 当签字字数过多,想增加下一页,那如何把两个或者多个控件截图后的pixelMap拼接为一个图片

HarmonyOS
18h前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
FengTianYa

可以参考以下示例:

Index.ets

import { common } from '@kit.AbilityKit';
import { BusinessError, request } from '@kit.BasicServicesKit';
import fs from '@ohos.file.fs';
import { fileUri } from '@kit.CoreFileKit';
import { joinPicture } from '../../utils/JoinPicture'
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { image } from '@kit.ImageKit';

@Entry
@Component
struct Index {
  @State imageUri: string = '';
  @State combineImageUri: string = '';
  context = getContext(this) as common.UIAbilityContext;
  filePath = this.context.filesDir + '/test.jpg';
  combinePath = this.context.filesDir + '/combine.jpg';
  saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性

  onPageShow(): void {
    this.checkImageExist()
  }

  checkImageExist(notExistCallback?: Function) {
    fs.access(this.filePath).then((res: boolean) => {
      if (res) {
        console.info("PictureJoinTogether file exists");
        this.imageUri = fileUri.getUriFromPath(this.filePath);
      } else {
        console.info("PictureJoinTogether file not exists");
        if (notExistCallback) {
          notExistCallback()
        }
      }
    }).catch((err: BusinessError) => {
      console.info("PictureJoinTogether access failed with error message: " + err.message + ", error code: " + err.code);
    });
  }

  // 只是为了分享图片,随便找个图片下载
  downloadFile(callback: Function) {
    this.checkImageExist(() => {
      request.downloadFile(this.context, {
        url: 'https://image.huawei.com/search/down?tn=download&word=download&ie=utf8&fr=detail&url=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2019-11-22%2F5dd7afa7df0fc.jpg&thumburl=https%3A%2F%2Fimg1.huawei.com%2Fit%2Fu%3D2205810988%2C4283060315%26fm%3D253%26fmt%3Dauto%26app%3D138%26f%3DJPEG%3Fw%3D800%26h%3D500',
        filePath: this.filePath,
        background: true
      }).then((downloadTask: request.DownloadTask) => {
        downloadTask.on('progress', (receivedSize: number, totalSize: number) => {
          console.info("PictureJoinTogether download receivedSize:" + receivedSize + " totalSize:" + totalSize);
        });

        downloadTask.on('complete', () => {
          console.info('PictureJoinTogether download complete');
          callback()
        })
      }).catch((err: BusinessError) => {
        console.error(`PictureJoinTogether Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
      });
    })
  }

  build() {
    Column({space: 10}) {
      Button('下载图片')
        .onClick(() => {
          if (this.imageUri !== '') {
            return;
          }
          this.downloadFile(() => {
            this.imageUri = fileUri.getUriFromPath(this.filePath);
          })
        })

      Image(this.imageUri)
        .width(200)
        .height(200)
        .backgroundColor(Color.Gray)

      Button('合并图片并保存到应用内')
        .onClick(() => {
          joinPicture.join([this.filePath, this.filePath], this.combinePath, () => {
            this.combineImageUri = fileUri.getUriFromPath(this.combinePath);
          })
        })

      Image(this.combineImageUri)
        .width(400)
        .height(200)
        .backgroundColor(Color.Gray)

      // 通过安全控件保存应用图片到图库
      SaveButton(this.saveButtonOptions)
        .onClick(async (event, result: SaveButtonOnClickResult) => {
          if (result === SaveButtonOnClickResult.SUCCESS) {
            try {
              const context = getContext();
              const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
              const uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
              console.info('createAsset successfully, uri: ' + uri);
              const file = await fs.open(uri, fs.OpenMode.READ_WRITE);
              const imageSource = image.createImageSource(this.combinePath);
              const imageInfo = await imageSource.getImageInfo();
              const opts: image.DecodingOptions = {
                editable: true,
                // 注意和拼接图片时的格式统一
                desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
                desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height }
              };
              // 需要通过packing打包图片,直接获取buffer并写入会导致图库中图片无法显示
              const pixelMap = await imageSource.createPixelMap(opts);
              const imagePackerApi: image.ImagePacker = image.createImagePacker();
              let packOpts: image.PackingOption = { format: "image/jpeg", quality: 100 };
              const packingArrayBuffer = await imagePackerApi.packing(pixelMap, packOpts);
              console.info('packing succeeded.');
              fs.writeSync(file.fd, packingArrayBuffer, { offset: 0 })
              fs.closeSync(file.fd)
            } catch (err) {
              console.error('createAsset failed, message = ', err);
            }
          } else {
            console.error('SaveButtonOnClickResult createAsset failed');
          }
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

JoinPicture.ets

import { image } from '@kit.ImageKit';
import fs from '@ohos.file.fs';

class JoinPicture {
  /**
   * RGBA与BGRA编码互换,前后统一编码后,就不需要转换了
   * @param data
   * @returns
   */
  rgba2BGRA(data: ArrayBuffer): ArrayBuffer {
    let length: number = data.byteLength;
    let tempBuffer: ArrayBuffer = new ArrayBuffer(length);
    let rgbaData = new DataView(data);
    let bgraData = new DataView(tempBuffer);
    for (let i = 0; i < length; i += 4) {
      bgraData.setUint8(i, rgbaData.getUint8(i + 2));
      bgraData.setUint8(i + 1, rgbaData.getUint8(i + 1));
      bgraData.setUint8(i + 2, rgbaData.getUint8(i));
      bgraData.setUint8(i + 3, rgbaData.getUint8(i + 3));
    }
    return bgraData.buffer
  }

  // 这里只考虑横向排列拼接,且每张小图的高度一致
  async join(picturePaths: Array<string>, joinPath: string, callback: Function) {
    try {
      if (picturePaths.length < 2) {
        console.info('PictureJoinTogether 需要拼接的图片数量不足')
        return;
      }
      const tempPath = picturePaths[0];
      const imageSource = image.createImageSource(tempPath);
      const imageInfo = await imageSource.getImageInfo();
      const singleWidth = imageInfo.size.width;
      const singleHeight = imageInfo.size.height;

      const combineOpts: image.InitializationOptions = {
        alphaType: 0,
        editable: true,
        // 注意上下格式统一
        pixelFormat: image.PixelMapFormat.BGRA_8888,
        size: { width: singleWidth * picturePaths.length, height: singleHeight }
      }
      const singleOpts: image.DecodingOptions = {
        editable: true,
        desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
        desiredSize: { width: singleWidth, height: singleHeight }
      };
      const combineColor = new ArrayBuffer(combineOpts.size.width * combineOpts.size.height * 4);
      let singleColor = new ArrayBuffer(singleOpts.desiredSize!.width * singleOpts.desiredSize!.height * 4);
      const newPixelMap = await image.createPixelMap(combineColor, combineOpts);
      for (let x = 0; x < picturePaths.length; x++) {
        //读取小图
        //图片应用沙箱路径
        let singlePath = picturePaths[x];
        let imageSource = image.createImageSource(singlePath);
        const singlePixelMap = await imageSource.createPixelMap(singleOpts);
        await singlePixelMap.readPixelsToBuffer(singleColor);
        //写入大图
        let area: image.PositionArea = {
          pixels: singleColor,
          offset: 0,
          stride: singleWidth * 4,
          region: {
            size: { height: singleHeight, width: singleWidth },
            x: singleWidth * x,
            y: 0
          }
        }
        await newPixelMap.writePixels(area);
      }
      let combinePixelMap = newPixelMap;
      //保存大图
      const saveResult = await this.save(combinePixelMap, joinPath)
      saveResult && callback();
    } catch (err) {
      console.error('PictureJoinTogether join error: ' + JSON.stringify(err))
    }
  }

  async save(pixelMap: image.PixelMap | undefined, path: string) {
    if (pixelMap === undefined) {
      return false;
    }
    const imagePackerApi: image.ImagePacker = image.createImagePacker();
    let packOpts: image.PackingOption = { format: "image/jpeg", quality: 100 };
    const packingArrayBuffer = await imagePackerApi.packing(pixelMap, packOpts);
    console.info('packing succeeded.');
    let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    fs.writeSync(file.fd, packingArrayBuffer, { offset: 0 })
    fs.closeSync(file.fd)
    return true
  }
}

export const joinPicture = new JoinPicture()

module.json5 :

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
      },
      {
        "name": "ohos.permission.WRITE_IMAGEVIDEO",
        "reason": "$string:app_name",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      }
    ]
  }
}

createAsset方法需要该权限,参考文档如下:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#createasset

分享
微博
QQ
微信
回复
16h前
相关问题
HarmonyOS 多张画布横向合成一张图片
31浏览 • 1回复 待解决
HarmonyOS 多张string图片合并成一张
7浏览 • 1回复 待解决
如何吸取一张图片的色值?
429浏览 • 1回复 待解决
如何将一张图片转化为PixelMapElement
10040浏览 • 1回复 待解决
HarmonyOS 获取手机最新的一张图片
36浏览 • 1回复 待解决
如何保存一张PNG图片到相册中
1876浏览 • 1回复 待解决