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 }