• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import fileio from '@ohos.fileio'
17import image from '@ohos.multimedia.image'
18import photoAccessHelper from '@ohos.file.photoAccessHelper'
19import common from '@ohos.app.ability.common'
20import prompt from '@ohos.promptAction';
21import {
22  BarcodeFormat,
23  MultiFormatWriter,
24  BitMatrix,
25  ZXingStringEncoding,
26  EncodeHintType,
27  MultiFormatReader,
28  DecodeHintType,
29  RGBLuminanceSource,
30  BinaryBitmap,
31  HybridBinarizer
32} from '@ohos/zxing';
33import DateTimeUtil from '../utils/DateTimeUtil'
34import { CameraService } from './CameraService'
35import { QRCodeScanConst, ImageAttribute, DecodeResultAttribute } from './QRCodeScanConst';
36import Logger from '../utils/Logger'
37import Want from '@ohos.app.ability.Want'
38import dataSharePredicates from '@ohos.data.dataSharePredicates'
39
40const TAG: string = 'QRCodeParser'
41let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
42
43/**
44 * 二维码解析器
45 */
46class QRCodeParser {
47
48  /**
49   * 解析从相机获取的二维码图片
50   *
51   * @param cameraService
52   * @param canvasContext
53   */
54  parseQRCodeImageFromCamera(cameraService: CameraService,
55                             imageComponentType?: image.ComponentType): void {
56    Logger.info("parseQRCodeImageFromCamera start")
57    this.parseQRCodeImageWithNameFromCamera(cameraService, imageComponentType);
58    Logger.info("parseQRCodeImageFromCamera end")
59  }
60
61  /**
62   * 解析从相机获取的二维码图片,指定文件名称
63   *
64   * @param cameraService
65   * @param canvasContext
66   */
67  parseQRCodeImageWithNameFromCamera(cameraService: CameraService,
68                                     imageComponentType?: image.ComponentType): void {
69    Logger.info("parseQRCodeImageWithNameFromCamera...")
70    cameraService.imageReceiver.on('imageArrival', async () => {
71      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival start")
72      // 从接收器获取下一个图像,并返回结果
73      let targetImage: image.Image = await cameraService.imageReceiver.readNextImage()
74      // 默认按JPEG格式处理
75      let imgComponentType = imageComponentType === undefined ? image.ComponentType.JPEG : imageComponentType
76      let imageComponent = await targetImage.getComponent(imgComponentType)
77      // 给每次接收的image资源赋予随机文件名
78      let fileName = this.getRandomFileName(QRCodeScanConst.IMG_FILE_PREFIX, QRCodeScanConst.IMG_SUFFIX_JPG)
79      // 将image的ArrayBuffer写入指定文件中,返回文件uri
80      let imageUri = await this.createPublicDirFileAsset(fileName, imageComponent.byteBuffer);
81      // 释放已读取的image资源,以便处理下一个资源
82      await targetImage.release()
83
84      // 解析二维码
85      let qrCodeParseRlt = await this.parseImageQRCode(imageUri);
86      if (!qrCodeParseRlt.isSucess) {
87        Logger.error("parseQRCodeImageWithNameFromCamera qrCodeParseRlt is null")
88        prompt.showToast({
89          message: $r('app.string.qrCodeNotRecognized')
90        })
91        return;
92      }
93      // 拼接解析结果
94      AppStorage.SetOrCreate(QRCodeScanConst.QR_CODE_PARSE_RESULT, qrCodeParseRlt.decodeResult);
95      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival end")
96    })
97  }
98
99  /**
100   * 解析图片二维码信息
101   * @param canvasContext
102   * @param imageSrc
103   */
104  async parseImageQRCode(imageSrc: string): Promise<DecodeResultAttribute> {
105    Logger.info(`parseImageQRCode start`);
106    let media = photoAccessHelper.getPhotoAccessHelper(AppStorage.get<common.UIAbilityContext>('context'));
107    let imagesIdFetchOp: photoAccessHelper.FetchOptions = {
108      fetchColumns: [],
109      predicates: predicates.equalTo('uri', imageSrc)
110    }
111    // 获取图片文件资源
112    let fetchIdFileResult = await media.getAssets(imagesIdFetchOp);
113    let fileIdAsset = await fetchIdFileResult.getFirstObject();
114    // 获取文件描述符
115    let fd = await fileIdAsset.open('RW');
116    // 获取PixelMap图片数据
117    let imageSource = image.createImageSource(fd, { sourceDensity: 120, sourceSize: { width: 120, height: 120 } })
118    let decodingOptions: image.DecodingOptions = {
119      sampleSize: 1,
120      editable: true,
121      desiredSize: { width: 120, height: 120 },
122      rotate: 0,
123      desiredPixelFormat: 3,
124      desiredRegion: { size: { height: 120, width: 120 }, x: 0, y: 0 },
125      index: 0
126    };
127    let pixMapData = await imageSource.createPixelMap(decodingOptions);
128    let pixelBytesNumber = pixMapData.getPixelBytesNumber();
129    let arrayBuffer: ArrayBuffer = new ArrayBuffer(pixelBytesNumber);
130    // 读取图像像素数据,结果写入ArrayBuffer里
131    await pixMapData.readPixelsToBuffer(arrayBuffer);
132    let int32Array = new Int32Array(arrayBuffer);
133    let luminanceSource = new RGBLuminanceSource(int32Array, 120, 120);
134    let binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
135    let mltiFormatReader = new MultiFormatReader();
136    let hints: Map<number, number[]> = new Map();
137    hints.set(DecodeHintType.POSSIBLE_FORMATS, [BarcodeFormat.QR_CODE]);
138    mltiFormatReader.setHints(hints);
139    try {
140      // 解析二维码
141      let decodeResult = mltiFormatReader.decode(binaryBitmap);
142      let decodeText = decodeResult.getText();
143      Logger.info(`parseImageQRCode end ${decodeText}`);
144      return { isSucess: true, decodeResult: decodeText };
145    } catch (err) {
146      let error = `The error is ${err}`;
147      Logger.info(`parseImageQRCode end`);
148      return { isSucess: false, decodeResult: error };
149    }
150  }
151  /**
152   * 获取图片的属性
153   * @param context
154   * @param imageSrc
155   */
156  async getImageSource(imageSrc: string): Promise<ImageAttribute> {
157    Logger.info(`getImageSource start`);
158    let media = photoAccessHelper.getPhotoAccessHelper(AppStorage.get<common.UIAbilityContext>('context'));
159    let imagesIdFetchOp: photoAccessHelper.FetchOptions = {
160      fetchColumns: ['width', 'height'],
161      predicates: predicates.equalTo('uri', imageSrc)
162    }
163    // 获取图片文件资源
164    let fetchIdFileResult = await media.getAssets(imagesIdFetchOp);
165    let fileIdAsset = await fetchIdFileResult.getFirstObject();
166    // 将字符串分割,下标为1的数据即为图片类型
167    let imgType = fileIdAsset.displayName.split('.')[1];
168    // 获取文件描述符
169    let fd = await fileIdAsset.open('RW');
170    let context = AppStorage.get<common.UIAbilityContext>('context');
171    // 获取当前时间
172    let time = new Date().getTime();
173    // 拼接路径
174    let imagePath = `${context?.cacheDir}/${time.toString()}_note.${imgType}`;
175    // 将图片copy到此路径当中
176    await fileio.copyFile(fd, imagePath);
177    // 创建一个图片源类
178    let imageSource = image.createImageSource(imagePath);
179    // 创建PixelMap数组
180    let pixelMap = await imageSource.createPixelMap();
181    // 4、关闭安fd,Asset
182    await fileIdAsset.close(fd);
183    return {
184      width: fileIdAsset.get('width') as number,
185      height: fileIdAsset.get('height') as number,
186      pixelMap: pixelMap
187    };
188  }
189  /**
190   * 在媒体公共资源目录下的创建指定类型的资源对象
191   */
192  async createPublicDirFileAsset(fileDisplayName: string,
193                                 fileByteBuffer: ArrayBuffer): Promise<string> {
194    Logger.info("createPublicDirFileAsset start")
195    // 获取mediaLibrary对象
196    let mediaLibraryObj = photoAccessHelper.getPhotoAccessHelper(AppStorage.get('context'))
197    let fileAsset = await mediaLibraryObj.createAsset(fileDisplayName)
198    // 拿到fileAccess资源对应的uri
199    let fileUri = fileAsset.uri
200    // 调用open方法打开这个资源对象 // TODO 为啥要打开?
201    let fd = await fileAsset.open('Rw')
202    // 将图片资源写入对应的fileAccess
203    await fileio.write(fd, fileByteBuffer)
204    // 关闭这个资源对象
205    await fileAsset.close(fd)
206    Logger.info("createPublicDirFileAsset end")
207    return fileUri
208  }
209
210  /**
211   * 根据时间生成随机文件名
212   *
213   * @param filePrefix 文件名前缀
214   * @param fileSuffix 文件名后缀
215   */
216  getRandomFileName(filePrefix: string, fileSuffix: string) {
217    let dateTimeUtil = new DateTimeUtil()
218    return filePrefix + `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}` + fileSuffix
219  }
220}
221
222export { QRCodeParser }