Skip to content

Commit 644301d

Browse files
committed
Uniform pattern for generating graphics
1 parent b82f75c commit 644301d

6 files changed

Lines changed: 24 additions & 45 deletions

File tree

QrCodeGenerator/BmpBuilder.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,10 @@
1010

1111
namespace Net.Codecrete.QrCodeGenerator
1212
{
13-
internal class BmpBuilder
13+
internal static class BmpBuilder
1414
{
15-
internal BmpBuilder(int size, bool[,] modules)
16-
{
17-
_size = size;
18-
_modules = modules;
19-
}
20-
21-
private readonly int _size;
22-
23-
// The modules of this QR code (false = light, true = dark).
24-
// Immutable after constructor finishes.
25-
private readonly bool[,] _modules;
26-
2715
[SuppressMessage("csharpsquid", "S3776")]
28-
internal byte[] ToBmpBitmap(int border, int scale, int foreground, int background)
16+
internal static byte[] ToImage(QrCode qrCode, int border, int scale, int foreground, int background)
2917
{
3018
if (scale < 1)
3119
{
@@ -37,7 +25,8 @@ internal byte[] ToBmpBitmap(int border, int scale, int foreground, int backgroun
3725
throw new ArgumentOutOfRangeException(nameof(border), border, "Border must be non-negative.");
3826
}
3927

40-
var dim = (_size + 2 * border) * scale;
28+
var size = qrCode.Size;
29+
var dim = (size + 2 * border) * scale;
4130

4231
if (dim > short.MaxValue)
4332
{
@@ -121,7 +110,7 @@ internal byte[] ToBmpBitmap(int border, int scale, int foreground, int backgroun
121110

122111
if (border > 0)
123112
{
124-
var scaledSize = _size * scale;
113+
var scaledSize = size * scale;
125114

126115
for (i = 0; i < aligned; ++i)
127116
{
@@ -144,7 +133,7 @@ internal byte[] ToBmpBitmap(int border, int scale, int foreground, int backgroun
144133
}
145134
}
146135

147-
for (y = 0; y < _size; ++y)
136+
for (y = 0; y < size; ++y)
148137
{
149138
int j;
150139
var yOffset = y * scale + scaledBorder;
@@ -162,13 +151,13 @@ internal byte[] ToBmpBitmap(int border, int scale, int foreground, int backgroun
162151
continue;
163152
}
164153

165-
if (x < border || x >= _size + border)
154+
if (x < border || x >= size + border)
166155
{
167156
px |= (byte)(1 << (7 - j));
168157
continue;
169158
}
170159

171-
px |= (byte)(_modules[(_size - y - 1), x - border] ? 0 : 1 << (7 - j));
160+
px |= (byte)(qrCode.GetModule(x - border, size - y - 1) ? 0 : 1 << (7 - j));
172161
}
173162

174163
buf[62 + i + yOffset * aligned] = px;

QrCodeGenerator/MatrixEncoder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private static BitMatrix CreatePattern(int patternIndex, int version)
162162

163163
// Ordered by mask-selection frequency (descending) so low-penalty patterns
164164
// set a tight lowestPenalty early, maximizing early-stop bailouts in
165-
// Penalty.CalculatePenalty(). See QrCodeGeneratorProfiling/README.md
165+
// Penalty.Calculate(). See QrCodeGeneratorProfiling/README.md
166166
// "Mask Pattern Selection".
167167
private static readonly int[] PatternEvaluationOrder = { 2, 3, 7, 4, 6, 5, 0, 1 };
168168

@@ -193,8 +193,8 @@ private static int ApplyBestPattern(BitMatrix modules, int version, int ecc, Enc
193193
scoringMatrix.Xor(mask);
194194

195195
var penalty = encodingInfo == null
196-
? Penalty.CalculatePenalty(scoringMatrix, lowestPenalty)
197-
: Penalty.CalculatePenaltyFully(scoringMatrix, ref encodingInfo.Penalties[pattern]);
196+
? Penalty.Calculate(scoringMatrix, lowestPenalty)
197+
: Penalty.CalculateFully(scoringMatrix, ref encodingInfo.Penalties[pattern]);
198198

199199
// undo pattern
200200
scoringMatrix.Xor(mask);

QrCodeGenerator/Penalty.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal static class Penalty
2929
// some value >= lowestPenaltySoFar (the partial sum at the point of bailout).
3030
// Either way, comparing the result against lowestPenaltySoFar with strict
3131
// less-than yields the correct mask-selection decision.
32-
internal static int CalculatePenalty(ScoringMatrix matrix, int lowestPenaltySoFar)
32+
internal static int Calculate(ScoringMatrix matrix, int lowestPenaltySoFar)
3333
{
3434
// Ordered by mean penalty contribution (descending) for early-stop
3535
// effectiveness; see QrCodeGeneratorProfiling/README.md "Penalty Contribution".
@@ -63,7 +63,7 @@ internal static int CalculatePenalty(ScoringMatrix matrix, int lowestPenaltySoFa
6363
}
6464

6565
// Calculates the total penalty score, fully evaluating all contributions and collecting the details in penaltyInfo.
66-
internal static int CalculatePenaltyFully(ScoringMatrix matrix, ref PenaltyScore penaltyInfo)
66+
internal static int CalculateFully(ScoringMatrix matrix, ref PenaltyScore penaltyInfo)
6767
{
6868
penaltyInfo.Blocks = Calc2By2Blocks(matrix.Rows);
6969
penaltyInfo.VerticalStreaks = CalcSameColor(matrix.Columns);

QrCodeGenerator/QrCode.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ public bool GetModule(int x, int y)
387387
/// <param name="background">The background color. Defaults to white.</param>
388388
public string ToSvgString(int border, string foreground = "#000000", string background = "#ffffff")
389389
{
390-
return SvgBuilder.ToSvgString(ToRectangles(), Size, border, foreground, background);
390+
return SvgBuilder.ToSvgString(this, border, foreground, background);
391391
}
392392

393393
/// <summary>
@@ -413,7 +413,7 @@ public string ToSvgString(int border, string foreground = "#000000", string back
413413
/// <exception cref="ArgumentOutOfRangeException">Thrown if border is negative</exception>
414414
public string ToGraphicsPath(int border = 0)
415415
{
416-
return SvgBuilder.ToGraphicsPath(ToRectangles(), border);
416+
return SvgBuilder.ToGraphicsPath(this, border);
417417
}
418418

419419
/// <summary>
@@ -461,7 +461,7 @@ public IReadOnlyList<QrRectangle> ToRectangles()
461461
/// <paramref name="scale"/> is less than 1 or the resulting image is wider than 32,768 pixels.</exception>
462462
public byte[] ToBmpBitmap(int border = 0, int scale = 1, int foreground = 0x000000, int background = 0xffffff)
463463
{
464-
return AsBmpBuilder().ToBmpBitmap(border, scale, foreground, background);
464+
return BmpBuilder.ToImage(this, border, scale, foreground, background);
465465
}
466466

467467
/// <summary>
@@ -492,15 +492,6 @@ public static int RgbColor(byte red, byte green, byte blue)
492492
#endregion
493493

494494

495-
#region Private helper methods
496-
497-
private BmpBuilder AsBmpBuilder()
498-
{
499-
return new BmpBuilder(Size, _modules.ToBoolArray());
500-
}
501-
502-
#endregion
503-
504495

505496
#region Constants and tables
506497

QrCodeGenerator/SvgBuilder.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,22 @@ namespace Net.Codecrete.QrCodeGenerator
1818
internal static class SvgBuilder
1919
{
2020
// Creates a complete SVG document for the given rectangles.
21-
internal static string ToSvgString(IReadOnlyList<QrRectangle> rectangles, int size, int border,
22-
string foreground, string background)
21+
internal static string ToSvgString(QrCode qrCode, int border, string foreground, string background)
2322
{
2423
if (border < 0)
2524
{
2625
throw new ArgumentOutOfRangeException(nameof(border), "Border must be non-negative");
2726
}
2827

29-
var dim = size + border * 2;
28+
var dim = qrCode.Size + border * 2;
3029
var sb = new StringBuilder()
3130
.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
3231
.Append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
3332
.Append($"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {dim} {dim}\" stroke=\"none\">\n")
3433
.Append($"\t<rect width=\"100%\" height=\"100%\" fill=\"{background}\"/>\n")
3534
.Append("\t<path d=\"");
3635

37-
AppendPath(sb, rectangles, border);
36+
AppendPath(sb, qrCode.ToRectangles(), border);
3837

3938
return sb
4039
.Append($"\" fill=\"{foreground}\"/>\n")
@@ -43,15 +42,15 @@ internal static string ToSvgString(IReadOnlyList<QrRectangle> rectangles, int si
4342
}
4443

4544
// Creates an SVG/XAML graphics path for the given rectangles.
46-
internal static string ToGraphicsPath(IReadOnlyList<QrRectangle> rectangles, int border)
45+
internal static string ToGraphicsPath(QrCode qrCode, int border)
4746
{
4847
if (border < 0)
4948
{
5049
throw new ArgumentOutOfRangeException(nameof(border), "Border must be non-negative");
5150
}
5251

5352
var path = new StringBuilder();
54-
AppendPath(path, rectangles, border);
53+
AppendPath(path, qrCode.ToRectangles(), border);
5554
return path.ToString();
5655
}
5756

QrCodeGeneratorTest/PenaltyTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,22 +335,22 @@ public void CalcColorBalance_BasePenalty([CombinatorialValues(21, 29, 37, 65, 12
335335
[InlineData(25, 0.4)]
336336
[InlineData(37, 0.55)]
337337
[InlineData(65, 0.5)]
338-
public void CalculatePenalty_EarlyStopHonorsContract(int size, double fillPercent)
338+
public void Calculate_EarlyStopHonorsContract(int size, double fillPercent)
339339
{
340340
var modules = Fill(size, fillPercent);
341341
var scoringMatrix = ScoringMatrix.From(modules);
342342

343343
// Baseline: int.MaxValue threshold disables early-stop, so the function
344344
// runs to completion and returns the exact penalty.
345-
var truePenalty = Penalty.CalculatePenalty(scoringMatrix, int.MaxValue);
345+
var truePenalty = Penalty.Calculate(scoringMatrix, int.MaxValue);
346346

347347
// Contract: for any threshold T, the returned value is either the exact
348348
// penalty (no bailout) or some partial sum >= T (bailout fired). Either
349349
// way, comparing the result against T with strict less-than yields the
350350
// same mask-selection decision as a full computation.
351351
for (var threshold = -10; threshold <= truePenalty + 50; threshold += 1)
352352
{
353-
var result = Penalty.CalculatePenalty(scoringMatrix, threshold);
353+
var result = Penalty.Calculate(scoringMatrix, threshold);
354354
Assert.True(result == truePenalty || result >= threshold,
355355
$"threshold={threshold}, result={result}, truePenalty={truePenalty}");
356356
}

0 commit comments

Comments
 (0)