import * as sharp from 'sharp' ; import { BitMatrix } from '@zxing/library' ; export default class SharpImage { public constructor ( private wrapper , private buffer , private width , private height ) { } public static async loadWithRotations ( path , rotations ) { const images = new Map < number , SharpImage > ( ) ; for ( const rotation of rotations ) { const image = await this . loadWithRotation ( path , rotation ) ; images . set ( rotation , image ) ; } return images ; } public static async loadWithRotation ( path , rotation ) { const wrapper = sharp ( path ) . raw ( ) ; const metadata = await wrapper . metadata ( ) ; if ( metadata . channels !== 3 && metadata . space !== 'srgb' ) { wrapper . toColorspace ( 'sRGB' ) ; } const { data , info } = await wrapper . rotate ( rotation ) . toBuffer ( { resolveWithObject : true } ) ; const grayscaleBuffer = SharpImage . toGrayscaleBuffer ( new Uint8ClampedArray ( data . buffer ) , info . width , info . height , info . channels ) ; return new SharpImage ( wrapper , grayscaleBuffer , info . width , info . height ) ; } public static load ( path , rotation ) { const wrapper = sharp ( path ) . raw ( ) ; return new SharpImage ( wrapper , undefined , undefined , undefined ) ; } public static async loadAsBitMatrix ( path ) { const wrapper = sharp ( path ) . raw ( ) ; const metadata = await wrapper . metadata ( ) ; if ( metadata . channels !== 3 ) { wrapper . toColorspace ( 'sRGB' ) ; } const { data , info } = await wrapper . toBuffer ( { resolveWithObject : true } ) ; const channels = info . channels ; const width = info . width ; const height = info . height ; const grayscaleBuffer = SharpImage . toGrayscaleBuffer ( new Uint8ClampedArray ( data . buffer ) , width , height , channels ) ; const matrix = new BitMatrix ( width , height ) ; for ( let y = 0 ; y < height ; y ++ ) { for ( let x = 0 ; x < width ; x ++ ) { const pixel = grayscaleBuffer [ y * width + x ] ; if ( pixel <= 0x7F ) { matrix . set ( x , y ) ; } } } return matrix ; } private static toGrayscaleBuffer ( imageBuffer , width , height , channels ) { const grayscaleBuffer = new Uint8ClampedArray ( width * height ) ; for ( let i = 0 , j = 0 , length = imageBuffer . length ; i < length ; i += channels , j ++ ) { let gray = undefined ; if ( channels > 3 ) { const alpha = imageBuffer [ i + 3 ] ; if ( alpha === 0 ) { gray = 0xFF ; } } if ( gray === undefined ) { const pixelR = imageBuffer [ i ] ; const pixelG = imageBuffer [ i + 1 ] ; const pixelB = imageBuffer [ i + 2 ] ; gray = ( 306 * pixelR + 601 * pixelG + 117 * pixelB + 0x200 ) >> 10 ; } grayscaleBuffer [ j ] = gray ; } return grayscaleBuffer ; } public save ( path ) { this . wrapper . toFile ( path ) . catch ( err => console . error ( err ) ) ; } public getWidth ( ) { return this . width ; } public getHeight ( ) { return this . height ; } public getRow ( y , row ) { for ( let j = 0 , i = y * this . width , end = ( y + 1 ) * this . width ; i !== end ; i ++ ) { row [ j ++ ] = this . buffer [ i ] ; } } public getMatrix ( ) { return this . buffer ; } }