import BitArray from './../../common/BitArray' ; import ErrorCorrectionLevel from './../decoder/ErrorCorrectionLevel' ; import Version from './../decoder/Version' ; import ByteMatrix from './ByteMatrix' ; import Exception from './../../Exception' ; import Integer from './../../util/Integer' ; import QRCode from './QRCode' ; import MaskUtil from './MaskUtil' ; author satorux google author dswitkin google export default class MatrixUtil { private constructor ( ) { } private static POSITION_DETECTION_PATTERN : Array < Int32Array > = Array . from ( [ Int32Array . from ( [ 1 , 1 , 1 , 1 , 1 , 1 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 0 , 0 , 0 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 1 , 1 , 1 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 1 , 1 , 1 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 1 , 1 , 1 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 0 , 0 , 0 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 1 , 1 , 1 , 1 , 1 , 1 ] ) , ] ) ; private static POSITION_ADJUSTMENT_PATTERN : Array < Int32Array > = Array . from ( [ Int32Array . from ( [ 1 , 1 , 1 , 1 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 0 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 1 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 0 , 0 , 0 , 1 ] ) , Int32Array . from ( [ 1 , 1 , 1 , 1 , 1 ] ) , ] ) ; private static POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE : Array < Int32Array > = Array . from ( [ Int32Array . from ( [ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 18 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 22 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , - 1 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 22 , 38 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 24 , 42 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 46 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 28 , 50 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 54 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 32 , 58 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , 62 , - 1 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 46 , 66 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 48 , 70 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 50 , 74 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 54 , 78 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 56 , 82 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 58 , 86 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , 62 , 90 , - 1 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 28 , 50 , 72 , 94 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 50 , 74 , 98 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 54 , 78 , 102 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 28 , 54 , 80 , 106 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 32 , 58 , 84 , 110 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 58 , 86 , 114 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , 62 , 90 , 118 , - 1 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 50 , 74 , 98 , 122 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 54 , 78 , 102 , 126 , - 1 ] ) , Int32Array . from ( [ 6 , 26 , 52 , 78 , 104 , 130 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 56 , 82 , 108 , 134 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , 60 , 86 , 112 , 138 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 58 , 86 , 114 , 142 , - 1 ] ) , Int32Array . from ( [ 6 , 34 , 62 , 90 , 118 , 146 , - 1 ] ) , Int32Array . from ( [ 6 , 30 , 54 , 78 , 102 , 126 , 150 ] ) , Int32Array . from ( [ 6 , 24 , 50 , 76 , 102 , 128 , 154 ] ) , Int32Array . from ( [ 6 , 28 , 54 , 80 , 106 , 132 , 158 ] ) , Int32Array . from ( [ 6 , 32 , 58 , 84 , 110 , 136 , 162 ] ) , Int32Array . from ( [ 6 , 26 , 54 , 82 , 110 , 138 , 166 ] ) , Int32Array . from ( [ 6 , 30 , 58 , 86 , 114 , 142 , 170 ] ) , ] ) ; private static TYPE_INFO_COORDINATES : Array < Int32Array > = Array . from ( [ Int32Array . from ( [ 8 , 0 ] ) , Int32Array . from ( [ 8 , 1 ] ) , Int32Array . from ( [ 8 , 2 ] ) , Int32Array . from ( [ 8 , 3 ] ) , Int32Array . from ( [ 8 , 4 ] ) , Int32Array . from ( [ 8 , 5 ] ) , Int32Array . from ( [ 8 , 7 ] ) , Int32Array . from ( [ 8 , 8 ] ) , Int32Array . from ( [ 7 , 8 ] ) , Int32Array . from ( [ 5 , 8 ] ) , Int32Array . from ( [ 4 , 8 ] ) , Int32Array . from ( [ 3 , 8 ] ) , Int32Array . from ( [ 2 , 8 ] ) , Int32Array . from ( [ 1 , 8 ] ) , Int32Array . from ( [ 0 , 8 ] ) , ] ) ; private static VERSION_INFO_POLY = 0x1f25 ; private static TYPE_INFO_POLY = 0x537 ; private static TYPE_INFO_MASK_PATTERN = 0x5412 ; public static clearMatrix ( matrix ) { matrix . clear ( 255 ) ; } public static buildMatrix ( dataBits , ecLevel , version , maskPattern , matrix ) { MatrixUtil . clearMatrix ( matrix ) ; MatrixUtil . embedBasicPatterns ( version , matrix ) ; MatrixUtil . embedTypeInfo ( ecLevel , maskPattern , matrix ) ; MatrixUtil . maybeEmbedVersionInfo ( version , matrix ) ; MatrixUtil . embedDataBits ( dataBits , maskPattern , matrix ) ; } public static embedBasicPatterns ( version , matrix ) { MatrixUtil . embedPositionDetectionPatternsAndSeparators ( matrix ) ; MatrixUtil . embedDarkDotAtLeftBottomCorner ( matrix ) ; MatrixUtil . maybeEmbedPositionAdjustmentPatterns ( version , matrix ) ; MatrixUtil . embedTimingPatterns ( matrix ) ; } public static embedTypeInfo ( ecLevel , maskPattern , matrix ) { const typeInfoBits = new BitArray ( ) ; MatrixUtil . makeTypeInfoBits ( ecLevel , maskPattern , typeInfoBits ) ; for ( let i = 0 , size = typeInfoBits . getSize ( ) ; i < size ; ++ i ) { const bit = typeInfoBits . get ( typeInfoBits . getSize ( ) - 1 - i ) ; const coordinates = MatrixUtil . TYPE_INFO_COORDINATES [ i ] ; const x1 = coordinates [ 0 ] ; const y1 = coordinates [ 1 ] ; matrix . setBoolean ( x1 , y1 , bit ) ; if ( i < 8 ) { const x2 = matrix . getWidth ( ) - i - 1 ; const y2 = 8 ; matrix . setBoolean ( x2 , y2 , bit ) ; } else { const x2 = 8 ; const y2 = matrix . getHeight ( ) - 7 + ( i - 8 ) ; matrix . setBoolean ( x2 , y2 , bit ) ; } } } public static maybeEmbedVersionInfo ( version , matrix ) { if ( version . getVersionNumber ( ) < 7 ) { return ; } const versionInfoBits = new BitArray ( ) ; MatrixUtil . makeVersionInfoBits ( version , versionInfoBits ) ; let bitIndex = 6 * 3 - 1 ; for ( let i = 0 ; i < 6 ; ++ i ) { for ( let j = 0 ; j < 3 ; ++ j ) { const bit = versionInfoBits . get ( bitIndex ) ; bitIndex -- ; matrix . setBoolean ( i , matrix . getHeight ( ) - 11 + j , bit ) ; matrix . setBoolean ( matrix . getHeight ( ) - 11 + j , i , bit ) ; } } } public static embedDataBits ( dataBits , maskPattern , matrix ) { let bitIndex = 0 ; let direction = - 1 ; let x = matrix . getWidth ( ) - 1 ; let y = matrix . getHeight ( ) - 1 ; while ( x > 0 ) { if ( x == 6 ) { x -= 1 ; } while ( y >= 0 && y < matrix . getHeight ( ) ) { for ( let i = 0 ; i < 2 ; ++ i ) { const xx = x - i ; if ( ! MatrixUtil . isEmpty ( matrix . get ( xx , y ) ) ) { continue ; } let bit ; if ( bitIndex < dataBits . getSize ( ) ) { bit = dataBits . get ( bitIndex ) ; ++ bitIndex ; } else { bit = false ; } if ( maskPattern !== 255 && MaskUtil . getDataMaskBit ( maskPattern , xx , y ) ) { bit = ! bit ; } matrix . setBoolean ( xx , y , bit ) ; } y += direction ; } direction = - direction ; y += direction ; x -= 2 ; } if ( bitIndex !== dataBits . getSize ( ) ) { throw new Exception ( Exception . WriterException , "Not all bits consumed: " + bitIndex + '/' + dataBits . getSize ( ) ) ; } } public static findMSBSet ( value ) { return 32 - Integer . numberOfLeadingZeros ( value ) ; } public static calculateBCHCode ( value , poly ) { if ( poly === 0 ) { throw new Exception ( Exception . IllegalArgumentException , "0 polynomial" ) ; } const msbSetInPoly = MatrixUtil . findMSBSet ( poly ) ; value <<= msbSetInPoly - 1 ; while ( MatrixUtil . findMSBSet ( value ) >= msbSetInPoly ) { value ^= poly << ( MatrixUtil . findMSBSet ( value ) - msbSetInPoly ) ; } return value ; } public static makeTypeInfoBits ( ecLevel , maskPattern , bits ) { if ( ! QRCode . isValidMaskPattern ( maskPattern ) ) { throw new Exception ( Exception . WriterException , "Invalid mask pattern" ) ; } const typeInfo = ( ecLevel . getBits ( ) << 3 ) | maskPattern ; bits . appendBits ( typeInfo , 5 ) ; const bchCode = MatrixUtil . calculateBCHCode ( typeInfo , MatrixUtil . TYPE_INFO_POLY ) ; bits . appendBits ( bchCode , 10 ) ; const maskBits = new BitArray ( ) ; maskBits . appendBits ( MatrixUtil . TYPE_INFO_MASK_PATTERN , 15 ) ; bits . xor ( maskBits ) ; if ( bits . getSize ( ) !== 15 ) { throw new Exception ( Exception . WriterException , "should not happen but we got: " + bits . getSize ( ) ) ; } } public static makeVersionInfoBits ( version , bits ) { bits . appendBits ( version . getVersionNumber ( ) , 6 ) ; const bchCode = MatrixUtil . calculateBCHCode ( version . getVersionNumber ( ) , MatrixUtil . VERSION_INFO_POLY ) ; bits . appendBits ( bchCode , 12 ) ; if ( bits . getSize ( ) !== 18 ) { throw new Exception ( Exception . WriterException , "should not happen but we got: " + bits . getSize ( ) ) ; } } private static isEmpty ( value ) { return value === 255 ; } private static embedTimingPatterns ( matrix ) { for ( let i = 8 ; i < matrix . getWidth ( ) - 8 ; ++ i ) { const bit = ( i + 1 ) % 2 ; if ( MatrixUtil . isEmpty ( matrix . get ( i , 6 ) ) ) { matrix . setNumber ( i , 6 , bit ) ; } if ( MatrixUtil . isEmpty ( matrix . get ( 6 , i ) ) ) { matrix . setNumber ( 6 , i , bit ) ; } } } private static embedDarkDotAtLeftBottomCorner ( matrix ) { if ( matrix . get ( 8 , matrix . getHeight ( ) - 8 ) === 0 ) { throw new Exception ( Exception . WriterException ) ; } matrix . setNumber ( 8 , matrix . getHeight ( ) - 8 , 1 ) ; } private static embedHorizontalSeparationPattern ( xStart , yStart , matrix ) { for ( let x = 0 ; x < 8 ; ++ x ) { if ( ! MatrixUtil . isEmpty ( matrix . get ( xStart + x , yStart ) ) ) { throw new Exception ( Exception . WriterException ) ; } matrix . setNumber ( xStart + x , yStart , 0 ) ; } } private static embedVerticalSeparationPattern ( xStart , yStart , matrix ) { for ( let y = 0 ; y < 7 ; ++ y ) { if ( ! MatrixUtil . isEmpty ( matrix . get ( xStart , yStart + y ) ) ) { throw new Exception ( Exception . WriterException ) ; } matrix . setNumber ( xStart , yStart + y , 0 ) ; } } private static embedPositionAdjustmentPattern ( xStart , yStart , matrix ) { for ( let y = 0 ; y < 5 ; ++ y ) { const patternY = MatrixUtil . POSITION_ADJUSTMENT_PATTERN [ y ] ; for ( let x = 0 ; x < 5 ; ++ x ) { matrix . setNumber ( xStart + x , yStart + y , patternY [ x ] ) ; } } } private static embedPositionDetectionPattern ( xStart , yStart , matrix ) { for ( let y = 0 ; y < 7 ; ++ y ) { const patternY = MatrixUtil . POSITION_DETECTION_PATTERN [ y ] ; for ( let x = 0 ; x < 7 ; ++ x ) { matrix . setNumber ( xStart + x , yStart + y , patternY [ x ] ) ; } } } private static embedPositionDetectionPatternsAndSeparators ( matrix ) { const pdpWidth = MatrixUtil . POSITION_DETECTION_PATTERN [ 0 ] . length ; MatrixUtil . embedPositionDetectionPattern ( 0 , 0 , matrix ) ; MatrixUtil . embedPositionDetectionPattern ( matrix . getWidth ( ) - pdpWidth , 0 , matrix ) ; MatrixUtil . embedPositionDetectionPattern ( 0 , matrix . getWidth ( ) - pdpWidth , matrix ) ; const hspWidth = 8 ; MatrixUtil . embedHorizontalSeparationPattern ( 0 , hspWidth - 1 , matrix ) ; MatrixUtil . embedHorizontalSeparationPattern ( matrix . getWidth ( ) - hspWidth , hspWidth - 1 , matrix ) ; MatrixUtil . embedHorizontalSeparationPattern ( 0 , matrix . getWidth ( ) - hspWidth , matrix ) ; const vspSize = 7 ; MatrixUtil . embedVerticalSeparationPattern ( vspSize , 0 , matrix ) ; MatrixUtil . embedVerticalSeparationPattern ( matrix . getHeight ( ) - vspSize - 1 , 0 , matrix ) ; MatrixUtil . embedVerticalSeparationPattern ( vspSize , matrix . getHeight ( ) - vspSize , matrix ) ; } private static maybeEmbedPositionAdjustmentPatterns ( version , matrix ) { if ( version . getVersionNumber ( ) < 2 ) { return ; } const index = version . getVersionNumber ( ) - 1 ; const coordinates = MatrixUtil . POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE [ index ] ; for ( let i = 0 , length = coordinates . length ; i != length ; i ++ ) { const y = coordinates [ i ] ; if ( y >= 0 ) { for ( let j = 0 ; j != length ; j ++ ) { const x = coordinates [ j ] ; if ( x >= 0 && MatrixUtil . isEmpty ( matrix . get ( x , y ) ) ) { MatrixUtil . embedPositionAdjustmentPattern ( x - 2 , y - 2 , matrix ) ; } } } } } }