30 private static class FinderMarker
38 { X, X, X, X, X, X, X },
39 { X, _, _, _, _, _, X },
40 { X, _, x, x, x, _, X },
41 { X, _, x, x, x, _, X },
42 { X, _, x, x, x, _, X },
43 { X, _, _, _, _, _, X },
44 { X, X, X, X, X, X, X }
48 private static class AlignmentMarker
64 private static readonly
char[] halfBlocks =
new char[] {
' ',
'▀',
'▄',
'█' };
65 private static readonly
char[] quarterBlocks =
new char[] {
' ',
'▘',
'▝',
'▀',
'▖',
'▌',
'▞',
'▛',
'▗',
'▚',
'▐',
'▜',
'▄',
'▙',
'▟',
'█' };
67 private readonly
int size;
68 private readonly
bool[,] defined;
79 throw new ArgumentException(
"Invalid size.", nameof(
Size));
82 this.defined = this.mask =
new bool[
Size,
Size];
91 int c = Matrix.GetLength(0);
92 if (Matrix.GetLength(1) != c)
93 throw new ArgumentException(
"Matrix not square.", nameof(Matrix));
95 if (c < 21 || c > 177 || ((c - 21) & 3) != 0)
96 throw new ArgumentException(
"Invalid matrix dimensions.", nameof(Matrix));
98 if (Mask.GetLength(0) != c || Mask.GetLength(1) != c)
99 throw new ArgumentException(
"Mask must be same dimension as matrix.", nameof(Mask));
104 this.defined = (
bool[,])Mask.Clone();
131 for (y = 0; y < 7; y++)
133 for (x = 0; x < 7; x++)
135 this.defined[y + Y, x + X] =
true;
136 this.dots[y + Y, x + X] = FinderMarker.Dots[y, x];
150 for (y = 0; y < 5; y++)
152 for (x = 0; x < 5; x++)
154 this.defined[y + Y, x + X] =
true;
155 this.dots[y + Y, x + X] = AlignmentMarker.Dots[y, x];
172 if (!this.defined[Y, X1])
174 this.defined[Y, X1] =
true;
175 this.dots[Y, X1] = Dot;
197 if (!this.defined[Y1, X])
199 this.defined[Y1, X] =
true;
200 this.dots[Y1, X] = Dot;
220 for (y = 0; y < this.size; y++)
222 for (x = 0; x < this.size; x++)
224 if (this.defined[y, x])
248 int x = this.size - 1;
249 int y = this.size - 1;
257 for (j = 0; j < 8; j++)
262 if (this.defined[y, x])
266 this.defined[y, x] =
true;
267 this.dots[y, x] = (b & 0x80) != 0 ?
DotType.CodeForeground :
DotType.CodeBackground;
300 if (y == this.size - 1)
328 StringBuilder sb =
new StringBuilder();
330 string s =
new string(
' ', 16 + (this.size << 1));
333 for (y = 0; y < 4; y++)
336 for (y = 0; y < this.size; y++)
340 for (x = 0; x < this.size; x++)
342 if (((
int)this.dots[y, x] & 1) != 0)
351 for (y = 0; y < 4; y++)
354 return sb.ToString();
366 StringBuilder sb =
new StringBuilder();
368 string s =
new string(
' ', 8 + this.size);
372 for (y = 0; y < 4; y++)
375 for (y = 0; y < this.size; y += 2)
381 for (x = 0; x < this.size; x++)
383 i = (int)this.dots[y, x] & 1;
385 if (y2 < this.size && ((
int)this.dots[y2, x] & 1) != 0)
388 sb.Append(halfBlocks[i]);
394 for (y = 0; y < 4; y++)
397 return sb.ToString();
409 StringBuilder sb =
new StringBuilder();
411 string s =
new string(
' ', 4 + (this.size + 1) >> 1);
418 for (y = 0; y < this.size; y += 2)
424 for (x = 0; x < this.size; x++)
428 i = (int)this.dots[y, x] & 1;
430 if (x2 < this.size && ((
int)this.dots[y, x2] & 1) != 0)
433 if (y2 < this.size && ((
int)this.dots[y2, x] & 1) != 0)
436 if (x2 < this.size && y2 < this.size && ((
int)this.dots[y2, x2] & 1) != 0)
439 sb.Append(quarterBlocks[i]);
448 return sb.ToString();
462 for (y = 0; y < this.size; y++)
467 for (x = 0; x < this.size; x++)
471 if (Prev == (b = ((
int)this.dots[y, x] & 1) != 0))
487 Prev = ((int)this.dots[y, x] & 1) != 0;
507 for (x = 0; x < this.size; x++)
512 for (y = 0; y < this.size; y++)
516 if (Prev == (b = ((
int)this.dots[y, x] & 1) != 0))
531 Prev = ((int)this.dots[y, x] & 1) != 0;
546 int x, y, c = this.size - 1;
550 for (y = 0; y < c; y++)
552 for (x = 0; x < c; x++)
554 b = (int)this.dots[y, x] & 1;
556 if (((
int)this.dots[y + 1, x] & 1) == b &&
557 ((int)this.dots[y, x + 1] & 1) == b &&
558 ((int)this.dots[y + 1, x + 1] & 1) == b)
578 for (y = 0; y < this.size; y++)
581 for (x = 0; x < this.size; x++)
583 b = ((int)this.dots[y, x] & 1) != 0;
635 for (x = 0; x < this.size; x++)
638 for (y = 0; y < this.size; y++)
640 b = ((int)this.dots[y, x] & 1) != 0;
693 for (y = 0; y < this.size; y++)
695 for (x = 0; x < this.size; x++)
697 if (((
int)this.dots[y, x] & 1) != 0)
704 int PercentDark = (100 * NrDark) / (NrDark + NrLight);
705 int Prev5 = (PercentDark / 5) * 5;
706 int Next5 = Prev5 += 5;
708 Prev5 = Math.Abs(Prev5 - 50) / 5;
709 Next5 = Math.Abs(Next5 - 50) / 5;
711 return Math.Min(Prev5, Next5) * 10;
754 public static bool Mask0(
int x,
int y) => ((x + y) & 1) == 0;
762 public static bool Mask1(
int _,
int y) => (y & 1) == 0;
770 public static bool Mask2(
int x,
int _) => (x % 3) == 0;
778 public static bool Mask3(
int x,
int y) => ((x + y) % 3) == 0;
786 public static bool Mask4(
int x,
int y) => (((x / 3) + (y / 2)) & 1) == 0;
794 public static bool Mask5(
int x,
int y) => ((x * y) & 1) + ((x * y) % 3) == 0;
802 public static bool Mask6(
int x,
int y) => ((((x * y) & 1) + ((x * y) % 3)) & 1) == 0;
810 public static bool Mask7(
int x,
int y) => ((((x + y) & 1) + ((x * y) % 3)) & 1) == 0;
817 this.mask = (
bool[,])this.defined.Clone();
828 this.dots = (
DotType[,])this.dots.Clone();
830 for (y = 0; y < this.size; y++)
832 for (x = 0; x < this.size; x++)
834 if (!this.mask[y, x] && Mask(x, y))
835 this.dots[y, x] = (
DotType)((int)this.dots[y, x] ^ 1);
849 public void WriteBits(uint Bits,
int X,
int Y,
int Dx,
int Dy,
int NrBits)
853 this.defined[Y, X] =
true;
854 this.dots[Y, X] = (Bits & 0x80000000) != 0 ?
DotType.CodeForeground :
DotType.CodeBackground;
869 return this.
ToRGBA(this.size + 8, this.size + 8);
879 public byte[]
ToRGBA(
int Width,
int Height)
881 byte[] Result =
new byte[Width * Height * 4];
882 int SourceSize = this.size + 8;
883 int HalfSourceSize = SourceSize >> 1;
884 int HalfWidth = Width >> 1;
885 int HalfHeight = Height >> 1;
886 int Left = (4 * Width + HalfSourceSize) / SourceSize;
887 int Top = (4 * Height + HalfSourceSize) / SourceSize;
888 int dx = ((SourceSize << 16) + HalfWidth) / Width;
889 int dy = ((SourceSize << 16) + HalfHeight) / Height;
895 for (imgY = srcY = 0; imgY < Height; imgY++)
899 if (imgY < Top || y >= this.size)
901 for (imgX = 0; imgX < Width; imgX++)
912 for (imgX = 0; imgX < Left; imgX++)
920 for (srcX = 0; imgX < Width; imgX++)
926 if (((
int)this.dots[y, x] & 1) != 0)
943 for (; imgX < Width; imgX++)
966 return this.
ToRGBA(this.size + 8, this.size + 8, Color, AntiAlias);
980 int RawLine = 4 * (Width + 1);
981 byte[] Raw =
new byte[AntiAlias ? (Width + 1) * (Height + 1) * 4 : Width * Height * 4];
982 byte[] Result = AntiAlias ?
new byte[Width * Height * 4] : Raw;
983 int SourceMargin = 4;
984 float SourceSize = this.size + (2 * SourceMargin);
985 float dx = SourceSize / Width;
986 float dy = SourceSize / Height;
987 float x0 = -SourceMargin - 0.5f;
988 float y0 = -SourceMargin - 0.5f;
1001 scale = 1.0f / this.size;
1003 for (imgY = i = 0, y = y0; imgY < Height; imgY++, y += dy)
1005 srcY = (int)Math.Floor(y);
1006 yOutside = srcY < 0 || srcY >= this.size;
1008 for (imgX = 0, x = x0; imgX < Width; imgX++, x += dx)
1010 srcX = (int)Math.Floor(x);
1012 if (yOutside || srcX < 0 || srcX >= this.size)
1015 Dot = this.dots[srcY, srcX];
1017 cl = Color(x * scale, y * scale, x - srcX, y - srcY, Dot);
1019 Raw[i++] = (byte)cl;
1021 Raw[i++] = (byte)cl;
1023 Raw[i++] = (byte)cl;
1025 Raw[i++] = (byte)cl;
1030 for (j = 4; j > 0; j--, i++)
1031 Raw[i] = Raw[i - 4];
1037 for (j = RawLine; j > 0; j--, i++)
1038 Raw[i] = Raw[i - RawLine];
1040 for (imgY = i = k = 0, y = y0; imgY < Height; imgY++, y += dy)
1042 srcY = (int)Math.Floor(y);
1046 for (imgX = 0, x = x0; imgX < Width; imgX++, x += dx)
1048 srcX = (int)Math.Floor(x);
1052 for (j = 4; j > 0; j--, i++)
1056 v3 = Raw[i + RawLine];
1057 v4 = Raw[i + RawLine + 4];
1059 f1 = px2 * v1 + px1 * v2;
1060 f2 = px2 * v3 + px1 * v4;
1062 v1 = (int)(f1 * py1 + f2 * py2 + 0.5f);
1063 Result[k++] = (byte)(v1 < 0 ? 0 : v1 > 255 ? 255 : v1);
Class used to compute a QR code matrix.
bool EncodeDataBits(byte[] Data)
Encodes data bits in free positions in the matrix.
int NrDefinedDots
Number of defined dots in the matrix.
void WriteBits(uint Bits, int X, int Y, int Dx, int Dy, int NrBits)
Writes bits to the matrix.
void ApplyMask(MaskFunction Mask)
Applies a mask on the matrix.
QrMatrix(int Size)
Class used to compute a QR code matrix.
int PenaltyBalance()
Calculates a penalty score based on the balance between dark and light dots.
int PenaltyHorizontalBands()
Calculates a penalty score based on horizontal bands of dots of the same color.
void DrawAlignmentMarker(int X, int Y)
Draws a Alignment marker pattern in the matrix.
static bool Mask7(int x, int y)
Mask function 7
int PenaltyBlocks()
Calculates a penalty score based on same colored blocks.
int PenaltyHorizontalFinderPattern()
Calculates a penalty score based on horizontal finder patterns found.
static bool Mask4(int x, int y)
Mask function 4
static bool Mask0(int x, int y)
Mask function 0
static bool Mask6(int x, int y)
Mask function 6
string ToFullBlockText()
Generates a text string representing the QR code using full block characters and spaces....
int NrUndefined
Number of undefined dots in the matrix.
int Penalty()
Calculates the total penalty score of the matrix.
int Penalty(MaskFunction Mask)
Calculates the total penalty score of the matrix.
DotType[,] Dots
Encoded dots.
QrMatrix(DotType[,] Matrix, bool[,] Mask)
Class used to compute a QR code matrix.
void HLine(int X1, int X2, int Y, DotType Dot, bool Dotted)
Draws a horizontal line in the matrix.
static bool Mask3(int x, int y)
Mask function 3
byte[] ToRGBA(int Width, int Height)
Converts the matrix to pixels, each pixel represented by 4 bytes in the order Red,...
string ToHalfBlockText()
Generates a text string representing the QR code using half block characters and spaces....
int Size
Size of the matrix (along each side).
void VLine(int X, int Y1, int Y2, DotType Dot, bool Dotted)
Draws a horizontal line in the matrix.
void DrawFinderMarker(int X, int Y)
Draws a Finder marker pattern in the matrix.
byte[] ToRGBA()
Converts the matrix to pixels, each pixel represented by 4 bytes in the order Red,...
int PenaltyVerticalFinderPattern()
Calculates a penalty score based on vertical finder patterns found.
bool[,] Defined
What parts of the mask has been defined.
static bool Mask2(int x, int _)
Mask function 2
static bool Mask5(int x, int y)
Mask function 5
void SaveMask()
Saves the currently defined dots as a mask.
int PenaltyVerticalBands()
Calculates a penalty score based on horizontal bands of dots of the same color.
static bool Mask1(int _, int y)
Mask function 1
byte[] ToRGBA(ColorFunction Color, bool AntiAlias)
Converts the matrix to pixels, each pixel represented by 4 bytes in the order Red,...
byte[] ToRGBA(int Width, int Height, ColorFunction Color, bool AntiAlias)
Converts the matrix to pixels, each pixel represented by 4 bytes in the order Red,...
string ToQuarterBlockText()
Generates a text string representing the QR code using quarter block characters and spaces....
delegate uint ColorFunction(float CodeX, float CodeY, float DotX, float DotY, DotType Type)
Delegate for QR-code color functions
delegate bool MaskFunction(int x, int y)
Delegate for mask functions
DotType
Type of dot in code.