Skip to content

Commit

Permalink
make it configurable whether rgba image buffer should be reused / ove…
Browse files Browse the repository at this point in the history
…rwritten
  • Loading branch information
danimoh committed Feb 16, 2019
1 parent 57ede70 commit 25e98b3
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ This data is in the same form as the [`ImageData`](https://developer.mozilla.org
- `height` - The height of the image you wish to decode.
- `options` (optional) - Additional options.
- `inversionAttempts` - (`attemptBoth` (default), `dontInvert`, `onlyInvert`, or `invertFirst`) - Should jsQR attempt to invert the image to find QR codes with white modules on black backgrounds instead of the black modules on white background. This option defaults to `attemptBoth` for backwards compatibility but causes a ~50% performance hit, and will probably be default to `dontInvert` in future versions.
- `canOverwriteImage` - (`true` (default) or `false`) - Specifies whether the image data can be overwritten for performance improvements or whether it should be kept untouched. If `true` the image buffer will be used internally to reduce additional memory allocation.
### Return value
If a QR is able to be decoded the library will return an object with the following keys.
Expand Down
39 changes: 28 additions & 11 deletions src/binarizer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,21 @@ class Matrix {
}
}

export function binarize(data: Uint8ClampedArray, width: number, height: number, returnInverted: boolean) {
export function binarize(data: Uint8ClampedArray, width: number, height: number, returnInverted: boolean,
canOverwriteImage: boolean) {
const pixelCount = width * height;
if (data.length !== pixelCount * 4) {
throw new Error("Malformed data passed to binarizer.");
}
// assign the greyscale and binary image within the rgba buffer as the rgba image will not be needed after conversion
let bufferOffset = 0;
// Convert image to greyscale
const greyScaleBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
bufferOffset += pixelCount;
const greyscalePixels = new Matrix(width, height, greyScaleBuffer);
let greyscaleBuffer: Uint8ClampedArray;
if (canOverwriteImage) {
greyscaleBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
bufferOffset += pixelCount;
}
const greyscalePixels = new Matrix(width, height, greyscaleBuffer);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const pixelPosition = (y * width + x) * 4;
Expand All @@ -51,8 +55,11 @@ export function binarize(data: Uint8ClampedArray, width: number, height: number,
const verticalRegionCount = Math.ceil(height / REGION_SIZE);
const blackPointsCount = horizontalRegionCount * verticalRegionCount;

const blackPointsBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, blackPointsCount);
bufferOffset += blackPointsCount;
let blackPointsBuffer: Uint8ClampedArray;
if (canOverwriteImage) {
blackPointsBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, blackPointsCount);
bufferOffset += blackPointsCount;
}
const blackPoints = new Matrix(horizontalRegionCount, verticalRegionCount, blackPointsBuffer);
for (let verticalRegion = 0; verticalRegion < verticalRegionCount; verticalRegion++) {
for (let hortizontalRegion = 0; hortizontalRegion < horizontalRegionCount; hortizontalRegion++) {
Expand Down Expand Up @@ -100,13 +107,23 @@ export function binarize(data: Uint8ClampedArray, width: number, height: number,
}
}

const binarizedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
bufferOffset += binarizedBuffer.byteLength;
const binarized = new BitMatrix(binarizedBuffer, width);
let binarized: BitMatrix;
if (canOverwriteImage) {
const binarizedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
bufferOffset += pixelCount;
binarized = new BitMatrix(binarizedBuffer, width);
} else {
binarized = BitMatrix.createEmpty(width, height);
}

let inverted: BitMatrix = null;
if (returnInverted) {
const invertedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
inverted = new BitMatrix(invertedBuffer, width);
if (canOverwriteImage) {
const invertedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);
inverted = new BitMatrix(invertedBuffer, width);
} else {
inverted = BitMatrix.createEmpty(width, height);
}
}

for (let verticalRegion = 0; verticalRegion < verticalRegionCount; verticalRegion++) {
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ function scan(matrix: BitMatrix): QRCode | null {

export interface Options {
inversionAttempts?: "dontInvert" | "onlyInvert" | "attemptBoth" | "invertFirst";
canOverwriteImage?: boolean;
}

const defaultOptions: Options = {
inversionAttempts: "attemptBoth",
canOverwriteImage: true,
};

function jsQR(data: Uint8ClampedArray, width: number, height: number, providedOptions: Options = {}): QRCode | null {
Expand All @@ -71,7 +73,7 @@ function jsQR(data: Uint8ClampedArray, width: number, height: number, providedOp

const shouldInvert = options.inversionAttempts === "attemptBoth" || options.inversionAttempts === "invertFirst";
const tryInvertedFirst = options.inversionAttempts === "onlyInvert" || options.inversionAttempts === "invertFirst";
const {binarized, inverted} = binarize(data, width, height, shouldInvert);
const {binarized, inverted} = binarize(data, width, height, shouldInvert, options.canOverwriteImage);
let result = scan(tryInvertedFirst ? inverted : binarized);
if (!result && (options.inversionAttempts === "attemptBoth" || options.inversionAttempts === "invertFirst")) {
result = scan(tryInvertedFirst ? binarized : inverted);
Expand Down

0 comments on commit 25e98b3

Please sign in to comment.