• 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 mediaLibrary from '@ohos.multimedia.mediaLibrary'
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'
38
39const TAG: string = 'QRCodeParser'
40
41/**
42 * 二维码解析器
43 */
44class QRCodeParser {
45
46  /**
47   * 解析从相机获取的二维码图片
48   *
49   * @param cameraService
50   * @param canvasContext
51   */
52  parseQRCodeImageFromCamera(cameraService: CameraService,
53                             imageComponentType?: image.ComponentType): void {
54    Logger.info("parseQRCodeImageFromCamera start")
55    this.parseQRCodeImageWithNameFromCamera(cameraService, imageComponentType);
56    Logger.info("parseQRCodeImageFromCamera end")
57  }
58
59  /**
60   * 解析从相机获取的二维码图片,指定文件名称
61   *
62   * @param cameraService
63   * @param canvasContext
64   */
65  parseQRCodeImageWithNameFromCamera(cameraService: CameraService,
66                                     imageComponentType?: image.ComponentType): void {
67    Logger.info("parseQRCodeImageWithNameFromCamera...")
68    cameraService.imageReceiver.on('imageArrival', async () => {
69      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival start")
70      // 从接收器获取下一个图像,并返回结果
71      let targetImage: image.Image = await cameraService.imageReceiver.readNextImage()
72      // 默认按JPEG格式处理
73      let imgComponentType = imageComponentType === undefined ? image.ComponentType.JPEG : imageComponentType
74      let imageComponent = await targetImage.getComponent(imgComponentType)
75      // 给每次接收的image资源赋予随机文件名
76      let fileName = this.getRandomFileName(QRCodeScanConst.IMG_FILE_PREFIX, QRCodeScanConst.IMG_SUFFIX_JPG)
77      // 将image的ArrayBuffer写入指定文件中,返回文件uri
78      let imageUri = await this.createPublicDirFileAsset(fileName, mediaLibrary.MediaType.IMAGE,
79        mediaLibrary.DirectoryType.DIR_IMAGE, imageComponent.byteBuffer);
80      // 释放已读取的image资源,以便处理下一个资源
81      await targetImage.release()
82
83      // 解析二维码
84      let qrCodeParseRlt = await this.parseImageQRCode(imageUri);
85      if (!qrCodeParseRlt.isSucess) {
86        Logger.error("parseQRCodeImageWithNameFromCamera qrCodeParseRlt is null")
87        prompt.showToast({
88          message: $r('app.string.qrCodeNotRecognized')
89        })
90        return;
91      }
92      // 拼接解析结果
93      AppStorage.SetOrCreate(QRCodeScanConst.QR_CODE_PARSE_RESULT, qrCodeParseRlt.decodeResult);
94      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival end")
95    })
96  }
97
98  /**
99   * 解析图片二维码信息
100   * @param canvasContext
101   * @param imageSrc
102   */
103  async parseImageQRCode(imageSrc: string): Promise<DecodeResultAttribute> {
104    Logger.info(`parseImageQRCode start`);
105    let media = mediaLibrary.getMediaLibrary(AppStorage.Get<common.UIAbilityContext>('context'));
106    let imagesIdFetchOp: mediaLibrary.MediaFetchOptions = {
107      selections: ``,
108      selectionArgs: [],
109      uri: imageSrc
110    };
111    // 获取图片文件资源
112    let fetchIdFileResult = await media.getFileAssets(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 = mediaLibrary.getMediaLibrary(AppStorage.Get<common.UIAbilityContext>('context'));
159    let imagesIdFetchOp: mediaLibrary.MediaFetchOptions = {
160      selections: ``,
161      selectionArgs: [],
162      uri: imageSrc
163    };
164    // 获取图片文件资源
165    let fetchIdFileResult = await media.getFileAssets(imagesIdFetchOp);
166    let fileIdAsset = await fetchIdFileResult.getFirstObject();
167    // 将字符串分割,下标为1的数据即为图片类型
168    let imgType = fileIdAsset.displayName.split('.')[1];
169    // 获取文件描述符
170    let fd = await fileIdAsset.open('RW');
171    let context = AppStorage.Get<common.UIAbilityContext>('context');
172    // 获取当前时间
173    let time = new Date().getTime();
174    // 拼接路径
175    let imagePath = `${context?.cacheDir}/${time.toString()}_note.${imgType}`;
176    // 将图片copy到此路径当中
177    await fileio.copyFile(fd, imagePath);
178    // 创建一个图片源类
179    let imageSource = image.createImageSource(imagePath);
180    // 创建PixelMap数组
181    let pixelMap = await imageSource.createPixelMap();
182    // 4、关闭安fd,Asset
183    await fileIdAsset.close(fd);
184    return { width: fileIdAsset.width, height: fileIdAsset.height, pixelMap: pixelMap };
185  }
186  /**
187   * 在媒体公共资源目录下的创建指定类型的资源对象
188   */
189  async createPublicDirFileAsset(fileDisplayName: string,
190                                 mediaType: mediaLibrary.MediaType,
191                                 directoryType: mediaLibrary.DirectoryType,
192                                 fileByteBuffer: ArrayBuffer): Promise<string> {
193    Logger.info("createPublicDirFileAsset start")
194    // 获取mediaLibrary对象
195    let mediaLibraryObj = mediaLibrary.getMediaLibrary(AppStorage.Get('context'))
196    // 获取媒体公共资源目录
197    let publicDir = await mediaLibraryObj.getPublicDirectory(directoryType)
198    // 使用资源类型、资源名称、和公共资源路径创建FileAccess对象
199    let fileAsset = await mediaLibraryObj.createAsset(mediaType, fileDisplayName, publicDir)
200    // 拿到fileAccess资源对应的uri
201    let fileUri = fileAsset.uri
202    // 调用open方法打开这个资源对象 // TODO 为啥要打开?
203    let fd = await fileAsset.open('Rw')
204    // 将图片资源写入对应的fileAccess
205    await fileio.write(fd, fileByteBuffer)
206    // 关闭这个资源对象
207    await fileAsset.close(fd)
208    Logger.info("createPublicDirFileAsset end")
209    return fileUri
210  }
211
212  /**
213   * 根据时间生成随机文件名
214   *
215   * @param filePrefix 文件名前缀
216   * @param fileSuffix 文件名后缀
217   */
218  getRandomFileName(filePrefix: string, fileSuffix: string) {
219    let dateTimeUtil = new DateTimeUtil()
220    return filePrefix + `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}` + fileSuffix
221  }
222}
223
224export { QRCodeParser }