Skip to content

Commit 0af571e

Browse files
committed
Penalty without base
1 parent 43c4514 commit 0af571e

2 files changed

Lines changed: 113 additions & 28 deletions

File tree

QrCodeGenerator/Penalty.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ internal static int CalcSameColor(BitMatrix modules)
125125
+ BitMatrix.PopCount(rs2) + BitMatrix.PopCount(rs3);
126126
}
127127

128-
return fiveWindowCount + 2 * run5StartCount;
128+
// each finder pattern contributes 2 streaks of 5, 2 streaks of 7 and 1 streak of 8
129+
return fiveWindowCount + 2 * run5StartCount - 3 * (2 * 3 + 2 * 5 + 6);
129130
}
130131

131132
internal static int Calc2By2Blocks(BitMatrix modules)
@@ -164,7 +165,8 @@ internal static int Calc2By2Blocks(BitMatrix modules)
164165
}
165166
}
166167

167-
return count * 3;
168+
// subtract the 3 x 4 blocks contributed by the finder patterns
169+
return (count - 4 * 3) * 3;
168170
}
169171

170172
private static ulong[] BuildEdgeMask(int validBits)
@@ -217,7 +219,7 @@ internal static int CalcFinderPattern(BitMatrix modules)
217219
}
218220
}
219221

220-
// The mandatory finder pattern should lead to 9 matches.
222+
// The mandatory finder pattern leads to 9 matches.
221223
// Subtract them.
222224
return (count - 9) * 40;
223225
}

QrCodeGeneratorTest/PenaltyTest.cs

Lines changed: 108 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ namespace Net.Codecrete.QrCodeGenerator.Test
1212
{
1313
public class PenaltyTest
1414
{
15+
// Base penalty for streaks (due to finder patterns)
16+
private const int BasePenaltyStreaks = 66;
17+
// Base penalty of 2 by 2 block (due to finder patterns)
18+
private const int BasePenalty2By2Blocks = 36;
19+
// Base penalty finder patterns
20+
private const int BasePenaltyFinderPattern = 360;
21+
1522
[Theory, CombinatorialData]
1623
public void CalcSameColor_NoStreaks([CombinatorialValues(17, 25, 37)] int size, [CombinatorialValues(false, true)] bool invert)
1724
{
@@ -30,7 +37,7 @@ public void CalcSameColor_NoStreaks([CombinatorialValues(17, 25, 37)] int size,
3037
}
3138

3239
// no streaks
33-
Assert.Equal(0, Penalty.CalcSameColor(modules));
40+
Assert.Equal(0 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
3441
}
3542

3643
[Theory, CombinatorialData]
@@ -50,7 +57,7 @@ public void CalcSameColor_StreakOfFive([CombinatorialValues(17, 25, 37)] int siz
5057
}
5158

5259
// 1 streak of 5
53-
Assert.Equal(3, Penalty.CalcSameColor(modules));
60+
Assert.Equal(3 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
5461
}
5562

5663
[Theory, CombinatorialData]
@@ -70,7 +77,7 @@ public void CalcSameColor_StreakOfFiveAtEnd([CombinatorialValues(17, 25, 37)] in
7077
}
7178

7279
// 1 streak of 5
73-
Assert.Equal(3, Penalty.CalcSameColor(modules));
80+
Assert.Equal(3 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
7481
}
7582

7683
[Theory, CombinatorialData]
@@ -90,7 +97,7 @@ public void CalcSameColor_LongStreakNotCountedMultipleTimes([CombinatorialValues
9097
}
9198

9299
// 1 streak of 9
93-
Assert.Equal(7, Penalty.CalcSameColor(modules));
100+
Assert.Equal(7 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
94101
}
95102

96103
[Theory]
@@ -115,7 +122,7 @@ public void CalcSameColor_StreakAcrossWordBoundary(int size, int startCol)
115122
modules.Set(startCol + 5, 3, false);
116123
}
117124

118-
Assert.Equal(3, Penalty.CalcSameColor(modules));
125+
Assert.Equal(3 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
119126
}
120127

121128
[Theory]
@@ -132,7 +139,7 @@ public void CalcSameColor_StreakAtRowEnd_LargeSize(int size)
132139
modules.Set(x, 4, true);
133140
}
134141

135-
Assert.Equal(3, Penalty.CalcSameColor(modules));
142+
Assert.Equal(3 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
136143
}
137144

138145
[Theory]
@@ -149,7 +156,7 @@ public void CalcSameColor_StreakAtRowStart_LargeSize(int size)
149156
modules.Set(x, 4, true);
150157
}
151158

152-
Assert.Equal(3, Penalty.CalcSameColor(modules));
159+
Assert.Equal(3 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
153160
}
154161

155162
[Theory]
@@ -167,6 +174,25 @@ public void CalcSameColor_NoSpuriousRunInPadding(int size)
167174
modules.Set(x, 2, false);
168175
}
169176

177+
Assert.Equal(0 - BasePenaltyStreaks, Penalty.CalcSameColor(modules));
178+
}
179+
180+
[Theory, CombinatorialData]
181+
public void CalcSameColor_BasePenalty([CombinatorialValues(21, 29, 37, 65, 129, 177)]int size)
182+
{
183+
var modules = CreateCheckerboardWithFinders(size);
184+
185+
// ensure the white area around the finder pattern does not extend into the data area
186+
modules.Set(7, 8, true);
187+
modules.Set(8, 7, true);
188+
modules.Set(7, size - 9, true);
189+
modules.Set(8, size - 8, true);
190+
modules.Set(size - 8, 8, true);
191+
modules.Set(size - 9, 7, true);
192+
193+
Assert.Equal(0, Penalty.CalcSameColor(modules));
194+
195+
modules.Transpose();
170196
Assert.Equal(0, Penalty.CalcSameColor(modules));
171197
}
172198

@@ -180,10 +206,10 @@ public void Calc2By2Blocks_NoBlocks([CombinatorialValues(17, 25, 37, 65, 129, 17
180206
}
181207

182208
// no blocks
183-
Assert.Equal(0, Penalty.Calc2By2Blocks(modules));
209+
Assert.Equal(0 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
184210

185211
Transpose(modules);
186-
Assert.Equal(0, Penalty.Calc2By2Blocks(modules));
212+
Assert.Equal(0 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
187213
}
188214

189215
[Theory, CombinatorialData]
@@ -209,10 +235,10 @@ public void Calc2By2Blocks_FindBlocks([CombinatorialValues(17, 25, 37, 65, 129,
209235
}
210236

211237
// 2 blocks
212-
Assert.Equal(6, Penalty.Calc2By2Blocks(modules));
238+
Assert.Equal(6 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
213239

214240
Transpose(modules);
215-
Assert.Equal(6, Penalty.Calc2By2Blocks(modules));
241+
Assert.Equal(6 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
216242
}
217243

218244
[Theory]
@@ -228,7 +254,7 @@ public void Calc2By2Blocks_BlockAtWordBoundary(int size)
228254
modules.Set(63, 6, true);
229255
modules.Set(64, 6, true);
230256

231-
Assert.Equal(3, Penalty.Calc2By2Blocks(modules));
257+
Assert.Equal(3 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
232258
}
233259

234260
[Theory]
@@ -246,7 +272,7 @@ public void Calc2By2Blocks_BlockAtLastValidPosition(int size)
246272
modules.Set(size - 2, size - 1, true);
247273
modules.Set(size - 1, size - 1, true);
248274

249-
Assert.Equal(3, Penalty.Calc2By2Blocks(modules));
275+
Assert.Equal(3 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
250276
}
251277

252278
[Theory]
@@ -260,7 +286,17 @@ public void Calc2By2Blocks_OverlappingBlocksCountedMultipleTimes(int size, int x
260286
{
261287
var modules = CreateCheckerboard(size);
262288
modules.FillRect(x, 4, 3, 3);
263-
Assert.Equal(12, Penalty.Calc2By2Blocks(modules));
289+
Assert.Equal(12 - BasePenalty2By2Blocks, Penalty.Calc2By2Blocks(modules));
290+
}
291+
292+
[Theory, CombinatorialData]
293+
public void Calc2By2Blocks_BasePenalty([CombinatorialValues(21, 29, 37, 65, 129, 177)]int size)
294+
{
295+
var modules = CreateCheckerboardWithFinders(size);
296+
Assert.Equal(0, Penalty.Calc2By2Blocks(modules));
297+
298+
modules.Transpose();
299+
Assert.Equal(0, Penalty.Calc2By2Blocks(modules));
264300
}
265301

266302
[Theory]
@@ -285,6 +321,16 @@ public void CalcColorBalance(int size, double percent, int expectedPenalty)
285321
Assert.Equal(expectedPenalty, Penalty.CalcColorBalance(modules));
286322
}
287323

324+
[Theory, CombinatorialData]
325+
public void CalcColorBalance_BasePenalty([CombinatorialValues(21, 29, 37, 65, 129, 177)]int size)
326+
{
327+
var modules = CreateCheckerboardWithFinders(size);
328+
Assert.Equal(0, Penalty.CalcColorBalance(modules));
329+
330+
modules.Transpose();
331+
Assert.Equal(0, Penalty.CalcColorBalance(modules));
332+
}
333+
288334
[Theory]
289335
[InlineData(25, 0.4)]
290336
[InlineData(37, 0.55)]
@@ -318,10 +364,10 @@ public void CalcFinderPattern_NoPenalty([CombinatorialValues(19, 25, 37)] int si
318364
DrawFinderPattern(modules, 3, 3, leading: 0, trailing: 3);
319365
DrawFinderPattern(modules, 8, 6, leading: 3, trailing: 0);
320366

321-
Assert.Equal(-360, Penalty.CalcFinderPattern(modules));
367+
Assert.Equal(0 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
322368

323369
Invert(modules);
324-
Assert.Equal(-360, Penalty.CalcFinderPattern(modules));
370+
Assert.Equal(0 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
325371
}
326372

327373
[Theory, CombinatorialData]
@@ -331,10 +377,10 @@ public void CalcFinderPattern_Single([CombinatorialValues(true, false)] bool isL
331377

332378
DrawFinderPattern(modules, 4, 3, leading: isLeading ? 4 : 1, trailing: isLeading ? 1 : 4);
333379

334-
Assert.Equal(-320, Penalty.CalcFinderPattern(modules));
380+
Assert.Equal(40 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
335381

336382
Invert(modules);
337-
Assert.Equal(-360, Penalty.CalcFinderPattern(modules));
383+
Assert.Equal(0 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
338384
}
339385

340386
[Theory]
@@ -354,10 +400,10 @@ public void CalcFinderPattern_SingleAtLeftBorder(int x, int y)
354400

355401
DrawFinderPattern(modules, x, y, leading: x, trailing: 1);
356402

357-
Assert.Equal(-320, Penalty.CalcFinderPattern(modules));
403+
Assert.Equal(40 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
358404

359405
Invert(modules);
360-
Assert.Equal(-360, Penalty.CalcFinderPattern(modules));
406+
Assert.Equal(0 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
361407
}
362408

363409
[Theory]
@@ -378,10 +424,20 @@ public void CalcFinderPattern_SingleAtRightBorder(int x, int y)
378424

379425
DrawFinderPattern(modules, size - x - 7, y, leading: 1, trailing: x);
380426

381-
Assert.Equal(-320, Penalty.CalcFinderPattern(modules));
427+
Assert.Equal(40 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
382428

383429
Invert(modules);
384-
Assert.Equal(-360, Penalty.CalcFinderPattern(modules));
430+
Assert.Equal(0 - BasePenaltyFinderPattern, Penalty.CalcFinderPattern(modules));
431+
}
432+
433+
[Theory, CombinatorialData]
434+
public void CalcFinderPattern_BasePenalty([CombinatorialValues(21, 29, 37, 65, 129, 177)]int size)
435+
{
436+
var modules = CreateCheckerboardWithFinders(size);
437+
Assert.Equal(0, Penalty.CalcFinderPattern(modules));
438+
439+
modules.Transpose();
440+
Assert.Equal(0, Penalty.CalcFinderPattern(modules));
385441
}
386442

387443
private static BitMatrix Fill(int size, double percent)
@@ -405,15 +461,42 @@ private static BitMatrix Fill(int size, double percent)
405461

406462
private static BitMatrix CreateCheckerboard(int size)
407463
{
408-
var result = new BitMatrix(size);
464+
var matrix = new BitMatrix(size);
409465
for (var y = 0; y < size; y += 1)
410466
{
411467
for (var x = y % 2; x < size; x += 2)
412468
{
413-
result.Set(x, y, true);
469+
matrix.Set(x, y, true);
414470
}
415471
}
416-
return result;
472+
return matrix;
473+
}
474+
475+
private static BitMatrix CreateCheckerboardWithFinders(int size)
476+
{
477+
var matrix = CreateCheckerboard(size);
478+
479+
matrix.Invert();
480+
matrix.FillRect(0, 0, 8, 8);
481+
matrix.FillRect(0, size - 8, 8, 8);
482+
matrix.FillRect(size - 8, 0, 8, 8);
483+
matrix.Invert();
484+
485+
matrix.FillRect(0, 0, 7, 7);
486+
matrix.FillRect(0, size - 7, 7, 7);
487+
matrix.FillRect(size - 7, 0, 7, 7);
488+
489+
matrix.Invert();
490+
matrix.FillRect(1, 1, 5, 5);
491+
matrix.FillRect(1, size - 6, 5, 5);
492+
matrix.FillRect(size - 6, 1, 5, 5);
493+
matrix.Invert();
494+
495+
matrix.FillRect(2, 2, 3, 3);
496+
matrix.FillRect(2, size - 5, 3, 3);
497+
matrix.FillRect(size - 5, 2, 3, 3);
498+
499+
return matrix;
417500
}
418501

419502
private static void Invert(BitMatrix modules)

0 commit comments

Comments
 (0)