@@ -23,7 +23,7 @@ internal static List<Rectangle> GetBlocks(QrCode qr)
2323 }
2424 }
2525
26- return FilterOutsideFinderArea ( rectangles , qr . Size ) ;
26+ return FilterNotInFinderArea ( rectangles , qr . Size ) ;
2727 }
2828
2929 internal static List < Rectangle > GetHorizontalStreaks ( QrCode qr )
@@ -56,7 +56,7 @@ internal static List<Rectangle> GetHorizontalStreaks(QrCode qr)
5656 }
5757 }
5858
59- return FilterOutsideFinderArea ( rectangles , qr . Size ) ;
59+ return FilterNotInFinderArea ( rectangles , qr . Size ) ;
6060 }
6161
6262
@@ -89,10 +89,95 @@ internal static List<Rectangle> GetVerticalStreaks(QrCode qr)
8989 rectangles . Add ( new Rectangle ( x , size - count , 1 , count ) ) ;
9090 }
9191 }
92- return FilterOutsideFinderArea ( rectangles , qr . Size ) ;
92+ return FilterNotInFinderArea ( rectangles , qr . Size ) ;
9393 }
9494
95- private static List < Rectangle > FilterOutsideFinderArea ( List < Rectangle > rectangles , int size )
95+ internal static List < Rectangle > GetHorizontalFinderPatterns ( QrCode qr )
96+ {
97+ return GetFinderPatterns ( qr , false ) ;
98+ }
99+
100+ internal static List < Rectangle > GetVerticalFinderPatterns ( QrCode qr )
101+ {
102+ return GetFinderPatterns ( qr , true ) ;
103+ }
104+
105+ // Locates finder-like patterns (1:1:3:1:1, i.e. the 7 modules "1011101")
106+ // with at least 4 white modules on one side and at least 1 on the other.
107+ // Modules outside the QR code are treated as white (the quiet zone).
108+ // When 'vertical' is true, the scan runs along columns instead of rows.
109+ private static List < Rectangle > GetFinderPatterns ( QrCode qr , bool vertical )
110+ {
111+ var rectangles = new List < Rectangle > ( ) ;
112+ var size = qr . Size ;
113+
114+ // The 7-module core spans positions p..p+6; the side checks look 4 modules
115+ // beyond each end, so the scan ranges over every position where the core
116+ // can sit while its required margins fall within the quiet zone or matrix.
117+ for ( var line = 0 ; line < size ; line ++ )
118+ {
119+ for ( var p = 0 ; p <= size - 7 ; p ++ )
120+ {
121+ if ( ! IsFinderCore ( qr , line , p , vertical ) )
122+ {
123+ continue ;
124+ }
125+
126+ var leftWhite = WhiteCount ( qr , line , p - 1 , - 1 , vertical ) ;
127+ var rightWhite = WhiteCount ( qr , line , p + 7 , 1 , vertical ) ;
128+ if ( ( leftWhite >= 4 && rightWhite >= 1 ) || ( leftWhite >= 1 && rightWhite >= 4 ) )
129+ {
130+ rectangles . Add ( vertical
131+ ? new Rectangle ( line , p , 1 , 7 )
132+ : new Rectangle ( p , line , 7 , 1 ) ) ;
133+ }
134+ }
135+ }
136+
137+ return FilterNotInFinderArea ( rectangles , size ) ;
138+ }
139+
140+ // Returns whether the 7 modules starting at 'p' match the core "1011101".
141+ private static bool IsFinderCore ( QrCode qr , int line , int p , bool vertical )
142+ {
143+ return Module ( qr , line , p , vertical )
144+ && ! Module ( qr , line , p + 1 , vertical )
145+ && Module ( qr , line , p + 2 , vertical )
146+ && Module ( qr , line , p + 3 , vertical )
147+ && Module ( qr , line , p + 4 , vertical )
148+ && ! Module ( qr , line , p + 5 , vertical )
149+ && Module ( qr , line , p + 6 , vertical ) ;
150+ }
151+
152+ // Counts consecutive white (and out-of-bounds) modules from 'start', stepping
153+ // by 'step', up to a maximum of 4 (the largest margin the rule cares about).
154+ private static int WhiteCount ( QrCode qr , int line , int start , int step , bool vertical )
155+ {
156+ var count = 0 ;
157+ for ( var i = 0 ; i < 4 ; i ++ )
158+ {
159+ if ( Module ( qr , line , start + i * step , vertical ) )
160+ {
161+ break ;
162+ }
163+ count ++ ;
164+ }
165+ return count ;
166+ }
167+
168+ // Reads a module, treating coordinates outside the QR code as white (false).
169+ private static bool Module ( QrCode qr , int line , int pos , bool vertical )
170+ {
171+ var x = vertical ? line : pos ;
172+ var y = vertical ? pos : line ;
173+ if ( x < 0 || y < 0 || x >= qr . Size || y >= qr . Size )
174+ {
175+ return false ;
176+ }
177+ return qr . GetModule ( x , y ) ;
178+ }
179+
180+ private static List < Rectangle > FilterNotInFinderArea ( List < Rectangle > rectangles , int size )
96181 {
97182 return [ .. rectangles . Where ( r => ! IsInFinderArea ( r . X , r . Y , r . Width , r . Height , size ) ) ] ;
98183 }
0 commit comments