UNPKG

zxing-typescript

Version:

TypeScript port of ZXing open-source, multi-format 1D/2D barcode image processing library

124 lines (105 loc) 4.75 kB
import InvertedLuminanceSource from './../core/InvertedLuminanceSource' import LuminanceSource from './../core/LuminanceSource' import Exception from './../core/Exception' export default class HTMLCanvasElementLuminanceSource extends LuminanceSource { private buffer: Uint8ClampedArray private static DEGREE_TO_RADIANS = Math.PI / 180 private tempCanvasElement: HTMLCanvasElement = null public constructor(private canvas: HTMLCanvasElement) { super(canvas.width, canvas.height) this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(canvas) } private static makeBufferFromCanvasImageData(canvas: HTMLCanvasElement): Uint8ClampedArray { const imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height) return HTMLCanvasElementLuminanceSource.toGrayscaleBuffer(imageData.data, canvas.width, canvas.height) } private static toGrayscaleBuffer(imageBuffer: Uint8ClampedArray, width: number, height: number): Uint8ClampedArray { const grayscaleBuffer = new Uint8ClampedArray(width * height) for(let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) { let gray const alpha = imageBuffer[i + 4] // 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 } return grayscaleBuffer } public getRow(y: number/*int*/, row: Uint8ClampedArray): Uint8ClampedArray { if (y < 0 || y >= this.getHeight()) { throw new Exception(Exception.IllegalArgumentException, "Requested row is outside the image: " + y) } const width: number/*int*/ = this.getWidth() const start = y * width if (row === null ) { row = this.buffer.slice(start, start + width) } else { if (row.length < width) { row = new Uint8ClampedArray(width) } // The underlying raster of image consists of bytes with the luminance values // TODO: can avoid set/slice? row.set(this.buffer.slice(start, start + width)) } return row } public getMatrix(): Uint8ClampedArray { return this.buffer } public isCropSupported(): boolean { return true } public crop(left: number/*int*/, top: number/*int*/, width: number/*int*/, height: number/*int*/): LuminanceSource { this.crop(left, top, width, height) return this } /** * This is always true, since the image is a gray-scale image. * * @return true */ public isRotateSupported(): boolean { return true } public rotateCounterClockwise(): LuminanceSource { this.rotate(-90) return this } public rotateCounterClockwise45(): LuminanceSource { this.rotate(-45) return this } private getTempCanvasElement() { if (null === this.tempCanvasElement) { const tempCanvasElement = this.canvas.ownerDocument.createElement('canvas') tempCanvasElement.style.width = `${this.canvas.width}px` tempCanvasElement.style.height = `${this.canvas.height}px` } return this.tempCanvasElement } private rotate(angle: number) { const tempCanvasElement = this.getTempCanvasElement() const tempContext = tempCanvasElement.getContext('2d') tempContext.rotate(angle * HTMLCanvasElementLuminanceSource.DEGREE_TO_RADIANS) tempContext.drawImage(this.canvas, 0, 0) this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(tempCanvasElement) return this } public invert(): LuminanceSource { return new InvertedLuminanceSource(this) } }