zxing-js/library

HTMLCanvasElementLuminanceSource.toGrayscaleBuffer() auto invert issues

Opened this issue · 1 comments

Describe the bug
When enabling TRY_HARDER I couldn't get horizontal scanning working. It would only work if the barcode was rotated 90 degrees (portrait). But disabling TRY_HARDER, it would go back to allowing me to scan horizontal barcodes again.

The issue is with HTMLCanvasElementLuminanceSource.toGrayscaleBuffer()

        static toGrayscaleBuffer(imageBuffer, width, height, doAutoInvert = false) {
            const grayscaleBuffer = new Uint8ClampedArray(width * height);
            HTMLCanvasElementLuminanceSource.FRAME_INDEX = !HTMLCanvasElementLuminanceSource.FRAME_INDEX;
            if (HTMLCanvasElementLuminanceSource.FRAME_INDEX || !doAutoInvert) {
                for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
                    let gray;
                    const alpha = imageBuffer[i + 3];
                    // The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
                    // black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
                    // barcode image. Force any such pixel to be white:
                    if (alpha === 0) {
                        gray = 0xFF;
                    }
                    else {
                        const pixelR = imageBuffer[i];
                        const pixelG = imageBuffer[i + 1];
                        const pixelB = imageBuffer[i + 2];
                        // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
                        // (306*R) >> 10 is approximately equal to R*0.299, and so on.
                        // 0x200 >> 10 is 0.5, it implements rounding.
                        gray = (306 * pixelR +
                            601 * pixelG +
                            117 * pixelB +
                            0x200) >> 10;
                    }
                    grayscaleBuffer[j] = gray;
                }
            }
            else {
                for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {
                    let gray;
                    const alpha = imageBuffer[i + 3];
                    // The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent
                    // black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a
                    // barcode image. Force any such pixel to be white:
                    if (alpha === 0) {
                        gray = 0xFF;
                    }
                    else {
                        const pixelR = imageBuffer[i];
                        const pixelG = imageBuffer[i + 1];
                        const pixelB = imageBuffer[i + 2];
                        // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
                        // (306*R) >> 10 is approximately equal to R*0.299, and so on.
                        // 0x200 >> 10 is 0.5, it implements rounding.
                        gray = (306 * pixelR +
                            601 * pixelG +
                            117 * pixelB +
                            0x200) >> 10;
                    }
                    grayscaleBuffer[j] = 0xFF - gray;
                }
            }
            return grayscaleBuffer;
        }

Which has the following line

HTMLCanvasElementLuminanceSource.FRAME_INDEX = !HTMLCanvasElementLuminanceSource.FRAME_INDEX;

Which makes the canvas capture invert every other frame ..

The issue is. If you enable TRY_HARDER, it does a second pass with a second decode. So the first try will be inverted, and the second try (by the TRY_HARDER flag) will not be inverted. This means the initial "normal" pass will always fail (as inverted), and the second rotated attempt by TRY_HARDER will always pass.

To Reproduce
Enable TRY_HARDER on a HTMLCanvasElement implementation

@jiapw I saw this was your commit. Was it to allow light on dark QR codes?