From 3693b9431bf38182c05dfa7976a800df8abfb70f Mon Sep 17 00:00:00 2001 From: Romeo Fragomeli Date: Fri, 10 Feb 2023 13:32:03 +0000 Subject: [PATCH] [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 --- .../webclient/barcode/ZXingBarcodeDetector.js | 34 ++++++++++++++++--- .../src/webclient/barcode/barcode_scanner.js | 29 ++++++++++++---- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js b/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js index d68281b6b59..b1952130d62 100644 --- a/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js +++ b/addons/web/static/src/webclient/barcode/ZXingBarcodeDetector.js @@ -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; + } } /** diff --git a/addons/web/static/src/webclient/barcode/barcode_scanner.js b/addons/web/static/src/webclient/barcode/barcode_scanner.js index 8da814e966e..752deef1ed3 100644 --- a/addons/web/static/src/webclient/barcode/barcode_scanner.js +++ b/addons/web/static/src/webclient/barcode/barcode_scanner.js @@ -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, {