Skip to content

Commit

Permalink
V2.3.4 (#632)
Browse files Browse the repository at this point in the history
* Scan alternatively between primary and secondary decoder

If secondary decoder is available.

Also,

`useBarCodeDetectorIfSupported` defaults to `true` and if supported the library will internally alternate between `BarcodeDetector` and `zxing-js`. Same robustness added for file based scan as well if more than one decoder is supported.

* Fixed ui issue#613

* Fix torch related bug

Fix for #634

Issue was related to track not getting updated on camera restart.

* Update changelog.md

* Scan image files at image resolution.

In case of `scanFile(..)` APIs, scan at image resolution. Show `Loading image...` while the image is being loaded for rendering.

* Update changelog.md

* misc documentation and PR fixes

* Update package.json
  • Loading branch information
mebjas authored Nov 30, 2022
1 parent 9c5c54c commit 9dfb1a3
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 78 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ interface Html5QrcodeConfigs {
* enable faster native code scanning experience.
*
* Set this flag to true, to enable using {@class BarcodeDetector} if
* supported. This is false by default.
* supported. This is true by default.
*
* Documentations:
* - https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
Expand Down
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### Version 2.3.4
- `useBarCodeDetectorIfSupported` defaults to `true` and if supported the library will internally alternate between `BarcodeDetector` and `zxing-js`. Same robustness added for file based scan as well if more than one decoder is supported.
- Fixed the UI issue - [Issue#613](https://github.com/mebjas/html5-qrcode/issues/613).
- Fix for torch issue - [Issue#634](https://github.com/mebjas/html5-qrcode/issues/634).
- In case of `scanFile(..)` APIs, scan at image resolution. Show `Loading image...` while the image is being loaded for rendering. More info at [Issue#612](https://github.com/mebjas/html5-qrcode/issues/612)

### Version 2.3.3
Quick fix for - [issue#621](https://github.com/mebjas/html5-qrcode/issues/621). With this zoom & torch is not supported in firefox for now.

Expand Down
45 changes: 1 addition & 44 deletions experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,7 @@ these features will get upgraded to general feature list.

## Using experimental native BarcodeDetector API

> **Note** This config has now been graduated to `Html5QrcodeConfigs` and deprecated from experimental config.
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
Setting `useBarCodeDetectorIfSupported` to `true` in `Html5QrcodeConfigs` will
enable this option.

#### Html5Qrcode class

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

let html5qrcode = new Html5Qrcode("reader", {
// Use this flag to turn on the feature.
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.
useBarCodeDetectorIfSupported: false
});
html5QrcodeScanner.render(onScanSuccess);
```
> **Update** This config has now been graduated to `Html5QrcodeConfigs` and deprecated from experimental config. This feature is now enabled by default.
### performance

Expand Down
2 changes: 1 addition & 1 deletion minified/html5-qrcode.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "html5-qrcode",
"version": "2.3.3",
"version": "2.3.4",
"description": "A cross platform HTML5 QR Code & bar code scanner",
"main": "./cjs/index.js",
"module": "./esm/index.js",
Expand Down
66 changes: 54 additions & 12 deletions src/code-decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
QrcodeResult,
Html5QrcodeSupportedFormats,
Logger,
QrcodeDecoderAsync
QrcodeDecoderAsync,
RobustQrcodeDecoderAsync,
} from "./core";

import { ZXingHtml5QrcodeDecoder } from "./zxing-html5-qrcode-decoder";
Expand All @@ -23,14 +24,16 @@ import { BarcodeDetectorDelegate } from "./native-bar-code-detector";
*
* Currently uses {@class ZXingHtml5QrcodeDecoder}, can be replace with another library.
*/
export class Html5QrcodeShim implements QrcodeDecoderAsync {
export class Html5QrcodeShim implements RobustQrcodeDecoderAsync {

private verbose: boolean;
private decoder: QrcodeDecoderAsync;
private primaryDecoder: QrcodeDecoderAsync;
private secondaryDecoder: QrcodeDecoderAsync | undefined;

private readonly EXECUTIONS_TO_REPORT_PERFORMANCE = 100;
private executions: number = 0;
private executionResults: Array<number> = [];
private wasPrimaryDecoderUsedInLastDecode = false;

public constructor(
requestedFormats: Array<Html5QrcodeSupportedFormats>,
Expand All @@ -42,26 +45,65 @@ export class Html5QrcodeShim implements QrcodeDecoderAsync {
// Use BarcodeDetector library if enabled by config and is supported.
if (useBarCodeDetectorIfSupported
&& BarcodeDetectorDelegate.isSupported()) {
this.decoder = new BarcodeDetectorDelegate(
this.primaryDecoder = new BarcodeDetectorDelegate(
requestedFormats, verbose, logger);
// If 'BarcodeDetector' is supported, the library will alternate
// between 'BarcodeDetector' and 'zxing-js' to compensate for
// quality gaps between the two.
this.secondaryDecoder = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);
} else {
this.decoder = new ZXingHtml5QrcodeDecoder(
this.primaryDecoder = new ZXingHtml5QrcodeDecoder(
requestedFormats, verbose, logger);
}
}

async decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult> {
let start = performance.now();
let startTime = performance.now();
try {
return await this.decoder.decodeAsync(canvas);
return await this.getDecoder().decodeAsync(canvas);
} finally {
if (this.verbose) {
let executionTime = performance.now() - start;
this.executionResults.push(executionTime);
this.executions++;
this.possiblyFlushPerformanceReport();
this.possiblyLogPerformance(startTime);
}
}

async decodeRobustlyAsync(canvas: HTMLCanvasElement)
: Promise<QrcodeResult> {
let startTime = performance.now();
try {
return await this.primaryDecoder.decodeAsync(canvas);
} catch(error) {
if (this.secondaryDecoder) {
// Try fallback.
return this.secondaryDecoder.decodeAsync(canvas);
}
throw error;
} finally {
this.possiblyLogPerformance(startTime);
}
}

private getDecoder(): QrcodeDecoderAsync {
if (!this.secondaryDecoder) {
return this.primaryDecoder;
}

if (this.wasPrimaryDecoderUsedInLastDecode === false) {
this.wasPrimaryDecoderUsedInLastDecode = true;
return this.primaryDecoder;
}
this.wasPrimaryDecoderUsedInLastDecode = false;
return this.secondaryDecoder;
}

private possiblyLogPerformance(startTime: number) {
if (!this.verbose) {
return;
}
let executionTime = performance.now() - startTime;
this.executionResults.push(executionTime);
this.executions++;
this.possiblyFlushPerformanceReport();
}

// Dumps mean decoding latency to console for last
Expand Down
18 changes: 18 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,24 @@ export interface QrcodeDecoderAsync {
decodeAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}

/**
* Code robust decoder interface.
*
* <p> A robust decoder may sacrifice latency of scanning for scanning quality.
* Ideal for file scan kind of operation.
*/
export interface RobustQrcodeDecoderAsync extends QrcodeDecoderAsync {
/**
* Decodes content of the canvas to find a valid QR code or bar code.
*
* <p>The method implementation will run the decoder more robustly at the
* expense of latency.
*
* @param canvas a valid html5 canvas element.
*/
decodeRobustlyAsync(canvas: HTMLCanvasElement): Promise<QrcodeResult>;
}

/** Interface for logger. */
export interface Logger {
log(message: string): void;
Expand Down
9 changes: 8 additions & 1 deletion src/html5-qrcode-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ function toHtml5QrcodeFullConfig(
};
}

// End to end scanner library.
export class Html5QrcodeScanner {

//#region private fields
Expand Down Expand Up @@ -533,6 +534,7 @@ export class Html5QrcodeScanner {
requestPermissionContainer: HTMLDivElement,
requestPermissionButton?: HTMLButtonElement) {
const $this = this;
$this.showHideScanTypeSwapLink(false);
$this.setHeaderMessage(
Html5QrcodeScannerStrings.cameraPermissionRequesting());

Expand All @@ -547,7 +549,7 @@ export class Html5QrcodeScanner {
// By this point the user has granted camera permissions.
$this.persistedDataManager.setHasPermission(
/* hasPermission */ true);

$this.showHideScanTypeSwapLink(true);
$this.resetHeaderMessage();
if (cameras && cameras.length > 0) {
scpCameraScanRegion.removeChild(requestPermissionContainer);
Expand Down Expand Up @@ -575,6 +577,7 @@ export class Html5QrcodeScanner {
}
$this.setHeaderMessage(
error, Html5QrcodeScannerStatus.STATUS_WARNING);
$this.showHideScanTypeSwapLink(true);
});
}

Expand Down Expand Up @@ -676,6 +679,7 @@ export class Html5QrcodeScanner {
return;
}

$this.setHeaderMessage(Html5QrcodeScannerStrings.loadingImage());
$this.html5Qrcode.scanFileV2(file, /* showImage= */ true)
.then((html5qrcodeResult: Html5QrcodeResult) => {
$this.resetHeaderMessage();
Expand Down Expand Up @@ -775,6 +779,9 @@ export class Html5QrcodeScanner {
Html5QrcodeScannerStatus.STATUS_WARNING);
}
);
} else {
torchButton.updateTorchCapability(
cameraCapabilities.torchFeature());
}
torchButton.show();
};
Expand Down
48 changes: 32 additions & 16 deletions src/html5-qrcode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
Html5QrcodeResultFactory,
Html5QrcodeErrorFactory,
Html5QrcodeSupportedFormats,
QrcodeDecoderAsync,
RobustQrcodeDecoderAsync,
isValidHtml5QrcodeSupportedFormats,
Html5QrcodeConstants,
Html5QrcodeResult,
Expand Down Expand Up @@ -55,6 +55,7 @@ class Constants extends Html5QrcodeConstants {
static DEFAULT_WIDTH = 300;
static DEFAULT_WIDTH_OFFSET = 2;
static FILE_SCAN_MIN_HEIGHT = 300;
static FILE_SCAN_HIDDEN_CANVAS_PADDING = 100;
static MIN_QR_BOX_SIZE = 50;
static SHADED_LEFT = 1;
static SHADED_RIGHT = 2;
Expand Down Expand Up @@ -87,7 +88,7 @@ export interface Html5QrcodeConfigs {
* enable faster native code scanning experience.
*
* Set this flag to true, to enable using {@class BarcodeDetector} if
* supported. This is false by default.
* supported. This is true by default.
*
* Documentations:
* - https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector
Expand Down Expand Up @@ -255,7 +256,7 @@ export class Html5Qrcode {
private readonly logger: Logger;
private readonly elementId: string;
private readonly verbose: boolean;
private readonly qrcode: QrcodeDecoderAsync;
private readonly qrcode: RobustQrcodeDecoderAsync;

private shouldScan: boolean;

Expand Down Expand Up @@ -681,27 +682,41 @@ export class Html5Qrcode {
/* dHeight= */ config.height);
}

// Hidden canvas should be at-least as big as the image.
// This could get really troublesome for large images like 12MP
// images or 48MP images captured on phone.
let padding = Constants.FILE_SCAN_HIDDEN_CANVAS_PADDING;
let hiddenImageWidth = Math.max(inputImage.width, config.width);
let hiddenImageHeight = Math.max(inputImage.height, config.height);

let hiddenCanvasWidth = hiddenImageWidth + 2 * padding;
let hiddenCanvasHeight = hiddenImageHeight + 2 * padding;

// Try harder for file scan.
// TODO(minhazav): Fallback to mirroring, 90 degree rotation and
// color inversion.
const hiddenCanvas = this.createCanvasElement(
config.width, config.height);
hiddenCanvasWidth, hiddenCanvasHeight);
element.appendChild(hiddenCanvas);
const context = hiddenCanvas.getContext("2d");
if (!context) {
throw "Unable to get 2d context from canvas";
}
context.canvas.width = config.width;
context.canvas.height = config.height;

context.canvas.width = hiddenCanvasWidth;
context.canvas.height = hiddenCanvasHeight;
context.drawImage(
inputImage,
/* sx= */ 0,
/* sy= */ 0,
/* sWidth= */ imageWidth,
/* sHeight= */ imageHeight,
/* dx= */ 0,
/* dy= */ 0,
/* dWidth= */ config.width,
/* dHeight= */ config.height);
/* dx= */ padding,
/* dy= */ padding,
/* dWidth= */ hiddenImageWidth,
/* dHeight= */ hiddenImageHeight);
try {
this.qrcode.decodeAsync(hiddenCanvas)
this.qrcode.decodeRobustlyAsync(hiddenCanvas)
.then((result) => {
resolve(
Html5QrcodeResultFactory.createFromQrcodeResult(
Expand Down Expand Up @@ -891,26 +906,27 @@ export class Html5Qrcode {
/*eslint complexity: ["error", 10]*/
private getUseBarCodeDetectorIfSupported(
config: Html5QrcodeConfigs | undefined) : boolean {
// Default value is true.
if (isNullOrUndefined(config)) {
return false;
return true;
}

if (!isNullOrUndefined(config!.useBarCodeDetectorIfSupported)) {
// Default value is false.
return config!.useBarCodeDetectorIfSupported === true;
return config!.useBarCodeDetectorIfSupported !== false;
}

if (isNullOrUndefined(config!.experimentalFeatures)) {
return false;
return true;
}

let experimentalFeatures = config!.experimentalFeatures!;
if (isNullOrUndefined(
experimentalFeatures.useBarCodeDetectorIfSupported)) {
return false;
return true;
}

return experimentalFeatures.useBarCodeDetectorIfSupported === true;
return experimentalFeatures.useBarCodeDetectorIfSupported !== false;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ export class Html5QrcodeScannerStrings {
public static zoom(): string {
return "zoom";
}

public static loadingImage(): string {
return "Loading image...";
}
}

/** Strings used in {@class LibraryInfoDiv} */
Expand Down
Loading

0 comments on commit 9dfb1a3

Please sign in to comment.