[IMP] web: add support for crop area for ZXingBarcodeDetector

As the lib ZXing barcode don't support scanning multiples barcodes at
once we have to find another way to match only the barcode into the
rectangle overlay.

Note:
Actually the barcode scanner have two ways to filter the barcode in the
rectangle overlay:
* native BarcodeDetector API -> we filter the barcode results by
 filtering the coordinate of the detected barcode (boundingBox)
* ZXing (lib) wrapper -> the lib scans only the cropped area from the
 source image (Camera)

Refs:
https://github.com/zxing-js/library/issues/346
https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector/detect#return_value

Part-of: odoo/odoo#112855
This commit is contained in:
Romeo Fragomeli
2023-02-17 12:07:20 +01:00
parent f9fd098f18
commit 3693b9431b
2 changed files with 53 additions and 10 deletions
@@ -66,12 +66,34 @@ export function buildZXingBarcodeDetector(ZXing) {
throw new DOMException("HTMLVideoElement is not ready", "InvalidStateError");
}
const canvas = document.createElement("canvas");
let barcodeArea;
if (this.cropArea && (this.cropArea.x || this.cropArea.y)) {
barcodeArea = this.cropArea;
} else {
barcodeArea = {
x: 0,
y: 0,
width: video.videoWidth,
height: video.videoHeight,
};
}
canvas.width = barcodeArea.width;
canvas.height = barcodeArea.height;
const ctx = canvas.getContext("2d");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
ctx.drawImage(
video,
barcodeArea.x,
barcodeArea.y,
barcodeArea.width,
barcodeArea.height,
0,
0,
barcodeArea.width,
barcodeArea.height
);
const luminanceSource = new ZXing.HTMLCanvasElementLuminanceSource(canvas);
const binaryBitmap = new ZXing.BinaryBitmap(new ZXing.HybridBinarizer(luminanceSource));
@@ -104,6 +126,10 @@ export function buildZXingBarcodeDetector(ZXing) {
throw err;
}
}
setCropArea(cropArea) {
this.cropArea = cropArea;
}
}
/**
@@ -95,6 +95,10 @@ export class BarcodeDialog extends Component {
});
}
isZXingBarcodeDetector() {
return this.detector && this.detector.__proto__.constructor.name === "ZXingBarcodeDetector";
}
/**
* Check for camera preview element readiness
*
@@ -113,6 +117,11 @@ export class BarcodeDialog extends Component {
onResize(overlayInfo) {
this.overlayInfo = overlayInfo;
if (this.isZXingBarcodeDetector()) {
// TODO need refactoring when ZXing will support multiple result in one scan
// https://github.com/zxing-js/library/issues/346
this.detector.setCropArea(this.adaptValuesWithRatio(this.overlayInfo, true));
}
}
/**
@@ -142,12 +151,8 @@ export class BarcodeDialog extends Component {
try {
const codes = await this.detector.detect(this.videoPreviewRef.el);
for (const code of codes) {
let { x, y, width, height } = code.boundingBox;
x *= this.zoomRatio;
y *= this.zoomRatio;
width *= this.zoomRatio;
height *= this.zoomRatio;
if (this.overlayInfo.x && this.overlayInfo.y) {
if (!this.isZXingBarcodeDetector() && this.overlayInfo.x && this.overlayInfo.y) {
const { x, y, width, height } = this.adaptValuesWithRatio(code.boundingBox);
if (
x < this.overlayInfo.x ||
x + width > this.overlayInfo.x + this.overlayInfo.width ||
@@ -164,6 +169,18 @@ export class BarcodeDialog extends Component {
this.onError(err);
}
}
adaptValuesWithRatio(object, dividerRatio = false) {
const newObject = Object.assign({}, object);
for (const key of Object.keys(newObject)) {
if (dividerRatio) {
newObject[key] /= this.zoomRatio;
} else {
newObject[key] *= this.zoomRatio;
}
}
return newObject;
}
}
Object.assign(BarcodeDialog, {