Skip to content

Commit fdeaca5

Browse files
authored
Merge pull request #614 from zxing-js/bugfix/527-scan-fail
#527 Unable to scan PDF_417 codes
2 parents f1c51a5 + c103ed1 commit fdeaca5

File tree

2 files changed

+64
-64
lines changed

2 files changed

+64
-64
lines changed

Diff for: src/browser/BrowserCodeReader.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -934,14 +934,17 @@ export class BrowserCodeReader {
934934
mediaElement: HTMLVisualMediaElement
935935
): BinaryBitmap {
936936
const ctx = this.getCaptureCanvasContext(mediaElement);
937-
if(mediaElement instanceof HTMLVideoElement) {
937+
// doing a scan with inverted colors on the second scan should only happen for video elements
938+
let doAutoInvert = false;
939+
if (mediaElement instanceof HTMLVideoElement) {
938940
this.drawFrameOnCanvas(<HTMLVideoElement>mediaElement);
941+
doAutoInvert = true;
939942
} else {
940943
this.drawImageOnCanvas(<HTMLImageElement>mediaElement);
941944
}
942945
const canvas = this.getCaptureCanvas(mediaElement);
943946

944-
const luminanceSource = new HTMLCanvasElementLuminanceSource(canvas);
947+
const luminanceSource = new HTMLCanvasElementLuminanceSource(canvas, doAutoInvert);
945948
const hybridBinarizer = new HybridBinarizer(luminanceSource);
946949

947950
return new BinaryBitmap(hybridBinarizer);

Diff for: src/browser/HTMLCanvasElementLuminanceSource.ts

+59-62
Original file line numberDiff line numberDiff line change
@@ -10,74 +10,71 @@ export class HTMLCanvasElementLuminanceSource extends LuminanceSource {
1010
private buffer: Uint8ClampedArray;
1111

1212
private static DEGREE_TO_RADIANS = Math.PI / 180;
13-
private static FRAME_INDEX = true;
13+
private static FRAME_INDEX = true;
1414

1515
private tempCanvasElement: HTMLCanvasElement = null;
1616

17-
public constructor(private canvas: HTMLCanvasElement) {
18-
super(canvas.width, canvas.height);
19-
this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(canvas);
17+
public constructor(private canvas: HTMLCanvasElement, doAutoInvert: boolean = false) {
18+
super(canvas.width, canvas.height);
19+
this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(canvas, doAutoInvert);
2020
}
2121

22-
private static makeBufferFromCanvasImageData(canvas: HTMLCanvasElement): Uint8ClampedArray {
23-
const imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
24-
return HTMLCanvasElementLuminanceSource.toGrayscaleBuffer(imageData.data, canvas.width, canvas.height);
22+
private static makeBufferFromCanvasImageData(canvas: HTMLCanvasElement, doAutoInvert: boolean = false): Uint8ClampedArray {
23+
const imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
24+
return HTMLCanvasElementLuminanceSource.toGrayscaleBuffer(imageData.data, canvas.width, canvas.height, doAutoInvert);
2525
}
2626

27-
private static toGrayscaleBuffer(imageBuffer: Uint8ClampedArray, width: number, height: number): Uint8ClampedArray {
28-
const grayscaleBuffer = new Uint8ClampedArray(width * height);
29-
HTMLCanvasElementLuminanceSource.FRAME_INDEX = !HTMLCanvasElementLuminanceSource.FRAME_INDEX;
30-
if(HTMLCanvasElementLuminanceSource.FRAME_INDEX)
31-
{
32-
for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
33-
let gray;
34-
const alpha = imageBuffer[i + 3];
35-
// The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
36-
// black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
37-
// barcode image. Force any such pixel to be white:
38-
if (alpha === 0) {
39-
gray = 0xFF;
40-
} else {
41-
const pixelR = imageBuffer[i];
42-
const pixelG = imageBuffer[i + 1];
43-
const pixelB = imageBuffer[i + 2];
44-
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
45-
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
46-
// 0x200 >> 10 is 0.5, it implements rounding.
47-
gray = (306 * pixelR +
48-
601 * pixelG +
49-
117 * pixelB +
50-
0x200) >> 10;
51-
}
52-
grayscaleBuffer[j] = gray;
53-
}
54-
}
55-
else
56-
{
57-
for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
58-
let gray;
59-
const alpha = imageBuffer[i + 3];
60-
// The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
61-
// black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
62-
// barcode image. Force any such pixel to be white:
63-
if (alpha === 0) {
64-
gray = 0xFF;
65-
} else {
66-
const pixelR = imageBuffer[i];
67-
const pixelG = imageBuffer[i + 1];
68-
const pixelB = imageBuffer[i + 2];
69-
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
70-
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
71-
// 0x200 >> 10 is 0.5, it implements rounding.
72-
gray = (306 * pixelR +
73-
601 * pixelG +
74-
117 * pixelB +
75-
0x200) >> 10;
76-
}
77-
grayscaleBuffer[j] = 0xFF - gray;
78-
}
79-
}
80-
return grayscaleBuffer;
27+
private static toGrayscaleBuffer(imageBuffer: Uint8ClampedArray, width: number, height: number, doAutoInvert: boolean = false): Uint8ClampedArray {
28+
const grayscaleBuffer = new Uint8ClampedArray(width * height);
29+
HTMLCanvasElementLuminanceSource.FRAME_INDEX = !HTMLCanvasElementLuminanceSource.FRAME_INDEX;
30+
if (HTMLCanvasElementLuminanceSource.FRAME_INDEX || !doAutoInvert) {
31+
for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
32+
let gray;
33+
const alpha = imageBuffer[i + 3];
34+
// The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
35+
// black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
36+
// barcode image. Force any such pixel to be white:
37+
if (alpha === 0) {
38+
gray = 0xFF;
39+
} else {
40+
const pixelR = imageBuffer[i];
41+
const pixelG = imageBuffer[i + 1];
42+
const pixelB = imageBuffer[i + 2];
43+
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
44+
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
45+
// 0x200 >> 10 is 0.5, it implements rounding.
46+
gray = (306 * pixelR +
47+
601 * pixelG +
48+
117 * pixelB +
49+
0x200) >> 10;
50+
}
51+
grayscaleBuffer[j] = gray;
52+
}
53+
} else {
54+
for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
55+
let gray;
56+
const alpha = imageBuffer[i + 3];
57+
// The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
58+
// black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
59+
// barcode image. Force any such pixel to be white:
60+
if (alpha === 0) {
61+
gray = 0xFF;
62+
} else {
63+
const pixelR = imageBuffer[i];
64+
const pixelG = imageBuffer[i + 1];
65+
const pixelB = imageBuffer[i + 2];
66+
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
67+
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
68+
// 0x200 >> 10 is 0.5, it implements rounding.
69+
gray = (306 * pixelR +
70+
601 * pixelG +
71+
117 * pixelB +
72+
0x200) >> 10;
73+
}
74+
grayscaleBuffer[j] = 0xFF - gray;
75+
}
76+
}
77+
return grayscaleBuffer;
8178
}
8279

8380
public getRow(y: number /*int*/, row: Uint8ClampedArray): Uint8ClampedArray {
@@ -167,4 +164,4 @@ export class HTMLCanvasElementLuminanceSource extends LuminanceSource {
167164
public invert(): LuminanceSource {
168165
return new InvertedLuminanceSource(this);
169166
}
170-
}
167+
}

0 commit comments

Comments
 (0)