Skip to content

Commit

Permalink
Add support for BarcodeDetector and refactors around it (#238)
Browse files Browse the repository at this point in the history
* Add support for BarcodeDetector and refactors around it

 TODOs
 - [ ] Check support / performance on Android / IOS device
 - [ ] Add additional documentation (Github / article) for experimental features
    - [ ] Link this to readme
    - [ ] Update demo page

Notes:
 - On Chrome `ZXing` based decoder takes `20-25` ms on my Mac book pro 16.
 - On Chrome `BarcodeDetector` based decoder takes `8.6-11 ms` on my Mac book pro 16.

* Fixes for codacy reported issues

* Add more documentation

* Link experimental read and fix Codacy issues

* Fix how support is checked for BarcodeDetector

Fix added for Safari.
Also, fixed some Codacy styling issues on readme

* fix codacy issues and update performance report

* fix table in markdown
  • Loading branch information
mebjas authored Jun 20, 2021
1 parent d671c5a commit a14ca5e
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 94 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,19 @@ Code scanning is dependent on [Zxing-js](https://github.com/zxing-js/library) li
| CODE_39| <img src="./assets/code_39.gif" > |
| CODE_93| <img src="./assets/code_93.gif" >|
| CODE_128| <img src="./assets/code_128.gif" >|
| MAXICODE| <img src="./assets/maxicode.gif" > |
| ITF| <img src="./assets/itf.png" >|
| EAN_13|<img src="./assets/ean13.jpeg" > |
| EAN_8| <img src="./assets/ean8.jpeg" >|
| PDF_417| <img src="./assets/pdf417.png" >|
| RSS_14| <img src="./assets/rss14.gif" >|
| RSS_EXPANDED|<img src="./assets/rssexpanded.gif" > |
| UPC_A| <img src="./assets/upca.jpeg" >|
| UPC_E| <img src="./assets/upce.jpeg" >|
| DATA_MATRIX|<img src="./assets/datamatrix.png" > |
| MAXICODE*| <img src="./assets/maxicode.gif" > |
| RSS_14*| <img src="./assets/rss14.gif" >|
| RSS_EXPANDED*|<img src="./assets/rssexpanded.gif" > |

> *Formats are not supported by our experimental integration with native
> BarcodeDetector API integration ([Read more](blob/master/experimental.md)).
## Description - [View Demo](https://blog.minhazav.dev/research/html5-qrcode.html)

Expand Down Expand Up @@ -425,7 +428,7 @@ interface Html5QrcodeCameraScanConfig {
*/
disableFlip?: boolean | undefined;

/**
/*
* Optional, @beta(this config is not well supported yet).
*
* Important: When passed this will override other parameters like
Expand Down Expand Up @@ -635,6 +638,13 @@ const html5QrcodeScanner = new Html5QrcodeScanner(
html5QrcodeScanner.render(onScanSuccess);
```

## Experimental features
The library now supports some experimental features which are supported in the
library but not recommended for production usage either due to limited testing
done or limited compatibility for underlying APIs used. Read more about it [here](blob/master/experimental.md).
Some of the experimental features include:
- [Support for BarcodeDetector javascript API](blob/master/experimental.md)

## How to modify and build
1. Code changes should only be made to [/src](./src) only.

Expand Down
28 changes: 25 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
### Version 2.0.11
- Add support for native [BarcodeDetector](https://web.dev/shape-detection/#barcodedetector) based scanning.
- On Chrome `ZXing` based decoder takes `20-25` ms on my Mac book pro 16.
- On Chrome `BarcodeDetector` based decoder takes `8.6-11 ms` on my Mac book pro 16.
```js
// How to enable
// Note: will only work if browser / OS supports this HTML api.
// Read more: https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector#browser_compatibility
function onScanSuccess(decodedText, decodedResult) {
// handle success.
}
let html5QrcodeScanner = new Html5QrcodeScanner(
"reader",
{
fps: 10,
qrbox: 250,
experimentalFeatures: {
useBarCodeDetectorIfSupported: true
}
});
html5QrcodeScanner.render(onScanSuccess);
```

### Version 2.0.10
- Migrate from assets hosted on Github to embedded base64 assets.
- Migrate from assets hosted on Github to embedded base64 assets.

### Version 2.0.9
- Added support for returning the type of code scanned (
[feature request](https://github.com/mebjas/html5-qrcode/issues/224))
- Added support for returning the type of code scanned ([feature request](https://github.com/mebjas/html5-qrcode/issues/224))

### Version 2.0.8
- Added support for configuring supported formats in `Html5Qrcode` & `Html5QrcodeScanner`.
Expand Down
4 changes: 2 additions & 2 deletions dist/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated code, do not modify manually.

- All files in this directory are minified from source code in the project hosted at [src](../src).
- Generated files should be supported in all major browsers.
- All files in this directory are minified from source code in the project hosted at [src](../src).
- Generated files should be supported in all major browsers.
2 changes: 1 addition & 1 deletion dist/html5-qrcode.min.js

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions experimental.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Experimental features
Some of the features that are supported by the library but not officially
recommended to be used across devices. If you find any of the features useful,
please feel free to use them at your own risk, they are configurable in the
library. In future, based on the support level and compatibility, some of
these features will get upgraded to general feature list.

## Using experimental native BarcodeDetector API
Turning on this flag allows using native [BarcodeDetector](https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector)
api now being introduced in web browsers for code scanning instead of `ZXing`
library we use officially.

### How to turn this on
It can be turned on using new config called `useBarCodeDetectorIfSupported`
added to `experimentalFeatures` config group. It's off (`value = false`) by
default. If set to on (`value = true`) and the `BarcodeDetector` is supported
by the browser, it'll be used for scanning all the kind of 1d and 2d codes.

#### Html5Qrcode class

```js
function onScanSuccess(decodedText, decodedResult) {
/** Handle success condition. */
}

let html5qrcode = new Html5Qrcode("reader", {
// Use this flag to turn on the feature.
experimentalFeatures: {
useBarCodeDetectorIfSupported: false
}
});

const scanConfig = { fps: 10, qrbox: 250 };
// If you want to prefer front camera
html5qrcode.start({ facingMode: "user" }, scanConfig, onScanSuccess);
```

#### Html5QrcodeScanner class

```js
function onScanSuccess(decodedText, decodedResult) {
/** Handle success condition. */
}

let html5QrcodeScanner = new Html5QrcodeScanner(
"reader",
{
fps: 10,
qrbox: 250,
// Use this flag to turn on the feature.
experimentalFeatures: {
useBarCodeDetectorIfSupported: false
}
});
html5QrcodeScanner.render(onScanSuccess);
```

### performance

Native scanning library seems to be giving better performance for scanning QR
codes than with Zxing library.

| Device | With ZXing | With BarcodeDecoder |
|--|--|--|
| Macbook Pro 16, Google Chrome | 21 ms | 10 ms|
| Pixel 4 Google Chrome | 56 ms | 23 ms |
| Pixel 4a Google Chrome | 92 ms| 47.3 ms |
| (low end) Android Device Google Chrome | 373 ms | 77.5 ms|

Not supported on:
- Macbook Pro 16, Safari (macOS Big Sur)

### More references
- [https://web.dev/shape-detection/#barcodedetector](https://web.dev/shape-detection/#barcodedetector)
36 changes: 21 additions & 15 deletions src/code-decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@
import {
QrcodeResult,
Html5QrcodeSupportedFormats,
QrcodeDecoder,
Logger
Logger,
QrcodeDecoderAsync
} from "./core";

import { ZXingHtml5QrcodeDecoder } from "./zxing-html5-qrcode-decoder";
import { BarcodeDetectorDelegate } from "./native-bar-code-detector";
import { ExperimentalFeaturesConfig } from "./experimental-features";

/**
* Shim layer for {@interface QrcodeDecoder}.
*
* Currently uses {@class ZXingHtml5QrcodeDecoder}, can be replace with another library.
*/
export class Html5QrcodeShim implements QrcodeDecoder {
export class Html5QrcodeShim implements QrcodeDecoderAsync {

private verbose: boolean;
private zxingDecorderDelegate: QrcodeDecoder;
private decoder: QrcodeDecoderAsync;

private readonly EXECUTIONS_TO_REPORT_PERFORMANCE = 100;
private executions: number = 0;
Expand All @@ -34,27 +36,31 @@ export class Html5QrcodeShim implements QrcodeDecoder {
public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
verbose: boolean,
logger: Logger) {
logger: Logger,
experimentalFeatureConfig: ExperimentalFeaturesConfig) {
this.verbose = verbose;
this.zxingDecorderDelegate = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);

// Use BarcodeDetector library if enabled by config and is supported.
if (experimentalFeatureConfig.useBarCodeDetectorIfSupported === true
&& BarcodeDetectorDelegate.isSupported()) {
this.decoder = new BarcodeDetectorDelegate(
requestedFormats, verbose, logger);
} else {
this.decoder = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);
}
}

decode(canvas: HTMLCanvasElement): QrcodeResult {
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
let start = performance.now();
try {
let result: QrcodeResult = this.zxingDecorderDelegate.decode(canvas);
return result;
} catch (ex) {
throw ex;
} finally {
return this.decoder.decodeAsync(canvas).finally(() => {
if (this.verbose) {
let executionTime = performance.now() - start;
this.executionResults.push(executionTime);
this.executions++;
this.possiblyFlushPerformanceReport();
}
}
});
}

// Dumps mean decoding latency to console for last
Expand Down
5 changes: 2 additions & 3 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,13 @@ export interface CameraDevice {
}

/** Code decoder interface. */
export interface QrcodeDecoder {

export interface QrcodeDecoderAsync {
/**
* Decodes content of the canvas to find a valid QR code or bar code.
*
* @param canvas a valid html5 canvas element.
*/
decode(canvas: HTMLCanvasElement): QrcodeResult;
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}

/** Interface for logger. */
Expand Down
62 changes: 62 additions & 0 deletions src/experimental-features.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @fileoverview
* Core library for experimental features.
*
* @author mebjas <[email protected]>
*
* Experimental features are those which have limited browser compatibility and
* hidden from official documentations. These features are not recommended by
* the author to be used in production unless explictly tested.
*
* Subset of the features are expected to upgrade to official feature list from
* time to time.
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/


/**
* Configuration for enabling or disabling experimental features in the library.
*
* These features will eventually upgrade as fully supported features in the
* library.
*/
export interface ExperimentalFeaturesConfig {
/**
* {@class BarcodeDetector} is being implemented by browsers at the moment.
* It has very limited browser support but as it gets available it could
* enable faster native code scanning experience.
*
* Set this flag to true, to enable using {@class BarcodeDetector} if
* supported. This is false by default.
*
* Documentations:
* - https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
* - https://web.dev/shape-detection/#barcodedetector
*/
useBarCodeDetectorIfSupported?: boolean | undefined;
}

/** Factory for {@interface ExperimentalFeaturesConfig}. */
export class ExperimentalFeaturesConfigFactory {

/**
* Creates fully filled experimental config.
*/
public static createExperimentalFeaturesConfig(
config?: ExperimentalFeaturesConfig | undefined)
: ExperimentalFeaturesConfig {
if (!config) {
return {
useBarCodeDetectorIfSupported: false
};
}

if (config.useBarCodeDetectorIfSupported !== true) {
config.useBarCodeDetectorIfSupported = false;
}

return config;
}
}
5 changes: 3 additions & 2 deletions src/html5-qrcode-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function toHtml5QrcodeFullConfig(
: Html5QrcodeFullConfig {
return {
formatsToSupport: config.formatsToSupport,
experimentalFeatures: config.experimentalFeatures,
verbose: verbose
};
}
Expand Down Expand Up @@ -366,7 +367,7 @@ export class Html5QrcodeScanner {
fileScanInput.disabled
= this.currentScanType === Html5QrcodeScanType.SCAN_TYPE_CAMERA;
const fileScanLabel = document.createElement("span");
fileScanLabel.innerHTML = "&nbsp; Select Image";
fileScanLabel.innerText = " Select Image";
fileBasedScanRegion.appendChild(fileScanInput);
fileBasedScanRegion.appendChild(fileScanLabel);
fileScanInput.addEventListener("change", (e: any) => {
Expand Down Expand Up @@ -411,7 +412,7 @@ export class Html5QrcodeScanner {

const cameraSelectionContainer = document.createElement("span");
cameraSelectionContainer.innerText
= `Select Camera (${cameras.length}) &nbsp;`;
= `Select Camera (${cameras.length}) `;
cameraSelectionContainer.style.marginRight = "10px";

const cameraSelectionSelect = document.createElement("select");
Expand Down
Loading

0 comments on commit a14ca5e

Please sign in to comment.