@ -596,31 +596,36 @@ public final class QrCode {
int result = 0 ;
int result = 0 ;
// Adjacent modules in row having same color, and finder-like patterns
// Adjacent modules in row having same color, and finder-like patterns
FinderPatternDetector det = new FinderPatternDetector ( ) ;
int [ ] runHistory = new int [ 7 ] ;
for ( int y = 0 ; y < size ; y + + ) {
for ( int y = 0 ; y < size ; y + + ) {
boolean runC lor = false ;
boolean runC o lor = false ;
int runX = 0 ;
int runX = 0 ;
det . reset ( ) ;
Arrays . fill ( runHistory , 0 ) ;
int padRun = size ; // Add white border to initial run
for ( int x = 0 ; x < size ; x + + ) {
for ( int x = 0 ; x < size ; x + + ) {
if ( modules [ y ] [ x ] = = runC lor) {
if ( modules [ y ] [ x ] = = runC o lor) {
runX + + ;
runX + + ;
if ( runX = = 5 )
if ( runX = = 5 )
result + = PENALTY_N1 ;
result + = PENALTY_N1 ;
else if ( runX > 5 )
else if ( runX > 5 )
result + + ;
result + + ;
} else {
} else {
runClor = modules [ y ] [ x ] ;
finderPenaltyAddHistory ( runX + padRun , runHistory ) ;
padRun = 0 ;
if ( ! runColor )
result + = finderPenaltyCountPatterns ( runHistory ) * PENALTY_N3 ;
runColor = modules [ y ] [ x ] ;
runX = 1 ;
runX = 1 ;
}
}
result + = det . addModuleAndMatch ( runClor ) * PENALTY_N3 ;
}
}
result + = det. terminateAndMatch ( ) * PENALTY_N3 ;
result + = finderPenaltyTerminateAndCount( runColor , runX + padRun , runHistory ) * PENALTY_N3 ;
}
}
// Adjacent modules in column having same color, and finder-like patterns
// Adjacent modules in column having same color, and finder-like patterns
for ( int x = 0 ; x < size ; x + + ) {
for ( int x = 0 ; x < size ; x + + ) {
det . reset ( ) ;
boolean runColor = false ;
boolean runColor = false ;
int runY = 0 ;
int runY = 0 ;
Arrays . fill ( runHistory , 0 ) ;
int padRun = size ; // Add white border to initial run
for ( int y = 0 ; y < size ; y + + ) {
for ( int y = 0 ; y < size ; y + + ) {
if ( modules [ y ] [ x ] = = runColor ) {
if ( modules [ y ] [ x ] = = runColor ) {
runY + + ;
runY + + ;
@ -629,12 +634,15 @@ public final class QrCode {
else if ( runY > 5 )
else if ( runY > 5 )
result + + ;
result + + ;
} else {
} else {
finderPenaltyAddHistory ( runY + padRun , runHistory ) ;
padRun = 0 ;
if ( ! runColor )
result + = finderPenaltyCountPatterns ( runHistory ) * PENALTY_N3 ;
runColor = modules [ y ] [ x ] ;
runColor = modules [ y ] [ x ] ;
runY = 1 ;
runY = 1 ;
}
}
result + = det . addModuleAndMatch ( runColor ) * PENALTY_N3 ;
}
}
result + = det. terminateAndMatch ( ) * PENALTY_N3 ;
result + = finderPenaltyTerminateAndCount( runColor , runY + padRun , runHistory ) * PENALTY_N3 ;
}
}
// 2*2 blocks of modules having same color
// 2*2 blocks of modules having same color
@ -724,6 +732,36 @@ public final class QrCode {
}
}
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
private int finderPenaltyCountPatterns ( int [ ] runHistory ) {
int n = runHistory [ 1 ] ;
assert n < = size * 3 ;
boolean core = n > 0 & & runHistory [ 2 ] = = n & & runHistory [ 3 ] = = n * 3 & & runHistory [ 4 ] = = n & & runHistory [ 5 ] = = n ;
return ( core & & runHistory [ 0 ] > = n * 4 & & runHistory [ 6 ] > = n ? 1 : 0 )
+ ( core & & runHistory [ 6 ] > = n * 4 & & runHistory [ 0 ] > = n ? 1 : 0 ) ;
}
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
private int finderPenaltyTerminateAndCount ( boolean currentRunColor , int currentRunLength , int [ ] runHistory ) {
if ( currentRunColor ) { // Terminate black run
finderPenaltyAddHistory ( currentRunLength , runHistory ) ;
currentRunLength = 0 ;
}
currentRunLength + = size ; // Add white border to final run
finderPenaltyAddHistory ( currentRunLength , runHistory ) ;
return finderPenaltyCountPatterns ( runHistory ) ;
}
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
private static void finderPenaltyAddHistory ( int currentRunLength , int [ ] runHistory ) {
System . arraycopy ( runHistory , 0 , runHistory , 1 , runHistory . length - 1 ) ;
runHistory [ 0 ] = currentRunLength ;
}
// Returns true iff the i'th bit of x is set to 1.
// Returns true iff the i'th bit of x is set to 1.
static boolean getBit ( int x , int i ) {
static boolean getBit ( int x , int i ) {
return ( ( x > > > i ) & 1 ) ! = 0 ;
return ( ( x > > > i ) & 1 ) ! = 0 ;
@ -882,118 +920,4 @@ public final class QrCode {
}
}
/*---- Private helper class ----*/
/ * *
* Detects finder - like patterns in a line of modules , for the purpose of penalty score calculation .
* A finder - like pattern has alternating black and white modules with run length ratios of 1 : 1 : 3 : 1 : 1 ,
* such that the center run is black and this pattern is surrounded by at least a ratio
* of 4 : 1 white modules on one side and at least 1 : 1 white modules on the other side .
* The finite line of modules is conceptually padded with an infinite number of white modules on both sides .
*
* Here are graphic examples of the designed behavior , where '[' means start of line ,
* ']' means end of line , '.' means white module , and '#' means black module :
* - [ . . . . # . # # # . # . . . . ] Two matches
* - [ # . # # # . # ] Two matches , because of infinite white border
* - [ # # . . # # # # # # . . # # ] Two matches , with a scale of 2
* - [ # . # # # . # . # ] One match , using the infinite white left border
* - [ # . # . # # # . # . # ] Zero matches , due to insufficient white modules surrounding the 1 : 1 : 3 : 1 : 1 pattern
* - [ # . # # # . # # ] Zero matches , because the rightmost black bar is too long
* - [ # . # # # . # . # # # . # ] Two matches , with the matches overlapping and sharing modules
* /
private static final class FinderPatternDetector {
/*-- Fields --*/
// Mutable running state
private boolean currentRunColor ; // false = white, true = black
private int currentRunLength ; // In modules, always positive
// runHistory[0] = length of most recently ended run,
// runHistory[1] = length of next older run of opposite color, etc.
// This array begins as all zeros. Zero is not a valid run length.
private int [ ] runHistory = new int [ 7 ] ;
/*-- Methods --*/
/ * *
* Re - initializes this detector to the start of a row or column .
* This allows reuse of this object and its array , reducing memory allocation .
* /
public void reset ( ) {
currentRunColor = false ;
currentRunLength = QR_CODE_SIZE_MAX ; // Add white border to initial run
Arrays . fill ( runHistory , 0 ) ;
}
/ * *
* Tells this detector that the next module has the specified color , and returns
* the number of finder - like patterns detected due to processing the current module .
* The result is usually 0 , but can be 1 or 2 only when transitioning from
* white to black ( i . e . { @code currentRunColor = = false & & color = = true } ) .
* @param color the color of the next module , where { @code true } denotes black and { @code false } is white
* @return either 0 , 1 , or 2
* /
public int addModuleAndMatch ( boolean color ) {
if ( color = = currentRunColor )
currentRunLength + + ;
else {
addToHistory ( currentRunLength ) ;
currentRunColor = color ;
currentRunLength = 1 ;
if ( color ) // Transitioning from white to black
return countCurrentMatches ( ) ;
}
return 0 ;
}
/ * *
* Tells this detector that the line of modules has ended , and
* returns the number of finder - like patterns detected at the end .
* After this , { @link # reset ( ) } must be called before any other methods .
* @return either 0 , 1 , or 2
* /
public int terminateAndMatch ( ) {
if ( currentRunColor ) { // Terminate black run
addToHistory ( currentRunLength ) ;
currentRunLength = 0 ;
}
currentRunLength + = QR_CODE_SIZE_MAX ; // Add white border to final run
addToHistory ( currentRunLength ) ;
return countCurrentMatches ( ) ;
}
// Shifts the array back and puts the given value at the front.
private void addToHistory ( int run ) {
System . arraycopy ( runHistory , 0 , runHistory , 1 , runHistory . length - 1 ) ;
runHistory [ 0 ] = run ;
}
// Can only be called immediately after a white run is added.
private int countCurrentMatches ( ) {
int n = runHistory [ 1 ] ;
assert n < = QR_CODE_SIZE_MAX * 3 ;
boolean core = n > 0 & & runHistory [ 2 ] = = n & & runHistory [ 3 ] = = n * 3 & & runHistory [ 4 ] = = n & & runHistory [ 5 ] = = n ;
if ( core ) {
return ( runHistory [ 0 ] > = n * 4 & & runHistory [ 6 ] > = n ? 1 : 0 )
+ ( runHistory [ 6 ] > = n * 4 & & runHistory [ 0 ] > = n ? 1 : 0 ) ;
} else
return 0 ;
}
/*-- Constant --*/
// This amount of padding is enough to guarantee at least 4 scaled
// white modules at any pattern scale that fits inside any QR Code.
private static final int QR_CODE_SIZE_MAX = QrCode . MAX_VERSION * 4 + 17 ;
}
}
}