自定义相机,使用createPixelMapFromSurface创建XComponent的截图和自定义扫码预览流XComponent中显示的不一致,createPixelMapFromSurface创建的截图不完整。

按照如下步骤进行排查:
[GetPreviewBufferMeta:648] surface buffer info width: 1280, height: 960, stride: 1280, actualSize: 1847296,
截图像素区域设置不正确导致截取的图片不对,问题代码如下:
获取的是XComponent的宽高而不是预览流的宽高。
.onAreaChange((oldValue: Area, newValue: Area) => {
this.xComponentWidth = newValue.width as number
this.xComponentHeight = newValue.height as number
})
width: this.xComponentWidth } } let region: image.Region = { x: 0, y: 0, size: { height: this.xComponentHeight, width: this.xComponentWidth } } 通过相机预览回调的宽高设置createPixelMapFromSurface的image.Region。
private frameCallback: AsyncCallback<customScan.ScanFrame> =
async (error: BusinessError, scanFrame: customScan.ScanFrame) => {
if (error) {
console.error(`Failed to get ScanFrame by callback. Code: ${error.code}, message: ${error.message}`);
return;
} else {
if (this.previewHeight === scanFrame.height && this.previewWidth === scanFrame.width) {
return;
}
this.previewHeight = scanFrame.height;
this.previewWidth = scanFrame.width;
// 通过setXComponentSurfaceRect设置surfaceRect,避免xComponent拉伸预览流
let aspectRatio = 1478 / 1216;
let resultRatio = scanFrame.width / scanFrame.height;
let surfaceWidth = 0;
let surfaceHeight = 0;
let offsetX = 0;
let offsetY = 0;
if (resultRatio <= aspectRatio) {
surfaceWidth = xComponentWidth;
surfaceHeight = xComponentWidth * resultRatio;
offsetX = 0;
offsetY = (xComponentHeight - surfaceHeight) / 2;
} else {
surfaceWidth = xComponentHeight / resultRatio;
surfaceHeight = xComponentHeight;
offsetX = (xComponentWidth - surfaceWidth) / 2;
offsetY = 0;
}
this.mXComponentController.setXComponentSurfaceRect({
offsetX,
offsetY,
surfaceWidth,
surfaceHeight
});
}
}; let region: image.Region =
{ x: 0, y: 0, size: { height: this.previewHeight, width: this.previewWidth } };
image.createPixelMapFromSurface(this.surfaceId, region).then(async (pixmap: image.PixelMap) => {
console.info('Succeeded in creating pixelmap from Surface');
await pixmap.rotate(90);
this.pixmap = pixmap;
}).catch((error: BusinessError) => {
console.error(`Failed to create pixelmap. code is ${error.code}, message is ${error.message}`);
}); 完整示例代码:
import { customScan, scanBarcode, scanCore } from '@kit.ScanKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
import { abilityAccessCtrl, bundleManager, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit';
import { AsyncCallback } from '@ohos.base';
const xComponentWidth = 1216;
const xComponentHeight = 1478;
const permissions: Array<Permissions> = ['ohos.permission.CAMERA'];
@Entry
@Component
struct CustomCameraPage {
private mXComponentController: XComponentController = new XComponentController();
private surfaceId?: string; //内部实现,不需要初始化
private viewControl?: customScan.ViewControl; //内部实现,不需要初始化
@State @Watch('startScan') isPageShow: boolean = false;
@State @Watch('startScan') isLoaded: boolean = false;
@State previewHeight: number = 400; //单位像素px
@State previewWidth: number = 400; //单位像素px
@State pixmap: PixelMap | undefined = undefined; //单位像素px
context = this.getUIContext().getHostContext() as common.UIAbilityContext;
async checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
// 获取应用程序的accessTokenID。
let tokenId: number = 0;
try {
let bundleInfo: bundleManager.BundleInfo =
await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to get bundle info for self, code: ${err.code}, message: ${err.message}`);
}
// 校验应用是否被授予权限。
try {
grantStatus = await atManager.checkAccessToken(tokenId, permission);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to check access token, code: ${err.code}, message: ${err.message}`);
}
return grantStatus;
}
startScan() {
if (this.isPageShow && this.isLoaded) {
if (this.viewControl != undefined) {
this.checkPermissionAndInitCamera();
}
} else {
this.releaseCamera();
}
}
releaseCamera() {
try {
customScan.release().then(() => {
console.info('Succeeded in releasing scan by promise');
}).catch((error: BusinessError) => {
console.error(`Failed to release scan by promise. Code: ${error.code}, message: ${error.message}`);
});
} catch (error) {
console.error(`Failed to release customScan. Code: ${error.code}, message: ${error.message}`);
}
}
private callback: AsyncCallback<Array<scanBarcode.ScanResult>> =
async (error: BusinessError, result: Array<scanBarcode.ScanResult>) => {
if (error) {
console.error(`Failed to get ScanResult by callback. Code: ${error.code}, message: ${error.message}`);
return;
}
console.info(`Succeeded in getting ScanResult by callback, result is ${JSON.stringify(result)}`);
};
// 回调获取ScanFrame
private frameCallback: AsyncCallback<customScan.ScanFrame> =
async (error: BusinessError, scanFrame: customScan.ScanFrame) => {
if (error) {
console.error(`Failed to get ScanFrame by callback. Code: ${error.code}, message: ${error.message}`);
return;
} else {
if (this.previewHeight === scanFrame.height && this.previewWidth === scanFrame.width) {
return;
}
this.previewHeight = scanFrame.height;
this.previewWidth = scanFrame.width;
// 通过setXComponentSurfaceRect设置surfaceRect,避免xComponent拉伸预览流
let aspectRatio = 1478 / 1216;
let resultRatio = scanFrame.width / scanFrame.height;
let surfaceWidth = 0;
let surfaceHeight = 0;
let offsetX = 0;
let offsetY = 0;
if (resultRatio <= aspectRatio) {
surfaceWidth = xComponentWidth;
surfaceHeight = xComponentWidth * resultRatio;
offsetX = 0;
offsetY = (xComponentHeight - surfaceHeight) / 2;
} else {
surfaceWidth = xComponentHeight / resultRatio;
surfaceHeight = xComponentHeight;
offsetX = (xComponentWidth - surfaceWidth) / 2;
offsetY = 0;
}
this.mXComponentController.setXComponentSurfaceRect({
offsetX,
offsetY,
surfaceWidth,
surfaceHeight
});
}
};
init() {
let options: scanBarcode.ScanOptions = {
scanTypes: [scanCore.ScanType.ALL],
enableMultiMode: false,
enableAlbum: false
};
//初始化相机
try {
customScan.init(options);
customScan.start(this.viewControl, this.callback, this.frameCallback);
} catch (error) {
console.error(`Failed to init. Code: ${error.code}, message: ${error.message}`);
}
}
//检查相机权限打开相机
async checkPermissionAndInitCamera() {
let cameraPermissionGrant = await this.checkPermissionGrant('ohos.permission.CAMERA');
if (cameraPermissionGrant === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
this.init();
} else {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
atManager.requestPermissionsFromUser(this.context, permissions).then((data: PermissionRequestResult) => {
let grantStatus: Array<number> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] != 0) {
atManager.requestPermissionOnSetting(this.context, permissions)
.then((data: Array<abilityAccessCtrl.GrantStatus>) => {
this.init();
console.info(`requestPermissionOnSetting success, result: ${data}`);
})
.catch((err: BusinessError) => {
console.error(`requestPermissionOnSetting fail, code: ${err.code}, message: ${err.message}`);
});
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
return;
}
}
this.init();
console.info(`Success to request permissions from user. authResults is ${grantStatus}.`);
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
});
}
}
onPageShow(): void {
this.isPageShow = true;
}
onPageHide(): void {
this.isPageShow = false;
}
build() {
Column() {
Row()
.width('100%')
.height(55)
Stack() {
XComponent({
type: XComponentType.SURFACE,
controller: this.mXComponentController
})
.id('root')
.onLoad(() => {
// 获取XComponent的surfaceId
this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
this.isLoaded = true;
this.viewControl = {
width: xComponentWidth,
height: xComponentHeight,
surfaceId: this.surfaceId,
};
this.checkPermissionAndInitCamera();
})
.width(xComponentWidth + 'px')
.height(xComponentHeight + 'px')
.enableAnalyzer(false)
}
.backgroundColor(Color.Transparent)
Row() {
Image(this.pixmap)
.objectFit(ImageFit.Contain)
.width('50%')
.height('100%')
}
.layoutWeight(1)
.padding(20)
Column() {
Row() {
Row() {
Button('拍照')
.width(70)
.onClick(() => {
let region: image.Region =
{ x: 0, y: 0, size: { height: this.previewHeight, width: this.previewWidth } };
image.createPixelMapFromSurface(this.surfaceId, region).then(async (pixmap: image.PixelMap) => {
console.info('Succeeded in creating pixelmap from Surface');
await pixmap.rotate(90);
this.pixmap = pixmap;
}).catch((error: BusinessError) => {
console.error(`Failed to create pixelmap. code is ${error.code}, message is ${error.message}`);
});
})
}
.justifyContent(FlexAlign.Center)
.layoutWeight(1)
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.padding({
top: 15,
bottom: 15
})
}
.width('100%')
}
.backgroundColor(Color.Black)
.width('100%')
.height('100%')
}
}