diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index 47244ba8..90d7239a 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Text; namespace QRCoder @@ -8,21 +10,142 @@ public partial class QRCodeGenerator /// /// Represents a polynomial, which is a sum of polynomial terms. /// - private struct Polynom + private struct Polynom : IDisposable { + private PolynomItem[] _polyItems; + private int _length; + /// /// Initializes a new instance of the struct with a specified number of initial capacity for polynomial terms. /// /// The initial capacity of the polynomial items list. public Polynom(int count) { - this.PolyItems = new List(count); + _length = 0; + _polyItems = RentArray(count); + } + + /// + /// Adds a polynomial term to the polynomial. + /// + public void Add(PolynomItem item) + { + AssertCapacity(_length + 1); + _polyItems[_length++] = item; + } + + /// + /// Removes the polynomial term at the specified index. + /// + public void RemoveAt(int index) + { + if ((uint)index >= (uint)_length) + throw new IndexOutOfRangeException(); + + if (index < _length - 1) + Array.Copy(_polyItems, index + 1, _polyItems, index, _length - index - 1); + + _length--; + } + + /// + /// Gets or sets a polynomial term at the specified index. + /// + public PolynomItem this[int index] + { + get { + if ((uint)index >= _length) + ThrowIndexOutOfRangeException(); + return _polyItems[index]; + } + set { + if ((uint)index >= _length) + ThrowIndexOutOfRangeException(); + _polyItems[index] = value; + } + } + +#if NET6_0_OR_GREATER + [StackTraceHidden] +#endif + private static void ThrowIndexOutOfRangeException() + { + throw new IndexOutOfRangeException(); + } + + + /// + /// Gets the number of polynomial terms in the polynomial. + /// + public int Count => _length; + + /// + /// Removes all polynomial terms from the polynomial. + /// + public void Clear() + { + _length = 0; } /// - /// Gets or sets the list of polynomial items, where each item represents a term in the polynomial. + /// Clones the polynomial, creating a new instance with the same polynomial terms. /// - public List PolyItems { get; set; } + public Polynom Clone() + { + var newPolynom = new Polynom(_length); + Array.Copy(_polyItems, newPolynom._polyItems, _length); + newPolynom._length = _length; + return newPolynom; + } + + /// + /// Sorts the collection of using a custom comparer function. + /// + /// + /// A function that compares two objects and returns an integer indicating their relative order: + /// less than zero if the first is less than the second, zero if they are equal, or greater than zero if the first is greater than the second. + /// + public void Sort(Func comparer) + { + if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + + var items = _polyItems; + if (items == null) throw new ObjectDisposedException(nameof(Polynom)); + + if (_length <= 1) + { + return; // Nothing to sort if the list is empty or contains only one element + } + + void QuickSort(int left, int right) + { + int i = left; + int j = right; + PolynomItem pivot = items[(left + right) / 2]; + + while (i <= j) + { + while (comparer(items[i], pivot) < 0) i++; + while (comparer(items[j], pivot) > 0) j--; + + if (i <= j) + { + // Swap items[i] and items[j] + PolynomItem temp = items[i]; + items[i] = items[j]; + items[j] = temp; + i++; + j--; + } + } + + // Recursively sort the sub-arrays + if (left < j) QuickSort(left, j); + if (i < right) QuickSort(i, right); + } + + QuickSort(0, _length - 1); + } /// /// Returns a string that represents the polynomial in standard algebraic notation. @@ -32,7 +155,7 @@ public override string ToString() { var sb = new StringBuilder(); - foreach (var polyItem in this.PolyItems) + foreach (var polyItem in _polyItems) { sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); } @@ -43,6 +166,137 @@ public override string ToString() return sb.ToString(); } + + /// + public void Dispose() + { + ReturnArray(_polyItems); + _polyItems = null; + } + + /// + /// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms. + /// + private void AssertCapacity(int min) + { + if (_polyItems.Length < min) + { + // All math by QRCoder should be done with fixed polynomials, so we don't need to grow the capacity. + ThrowNotSupportedException(); + + // Sample code for growing the capacity: + //var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 + //Array.Copy(_polyItems, newArray, _length); + //ReturnArray(_polyItems); + //_polyItems = newArray; + } + +#if NET6_0_OR_GREATER + [StackTraceHidden] +#endif + void ThrowNotSupportedException() + { + throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased."); + } + } + +#if NETCOREAPP + /// + /// Rents memory for the polynomial terms from the shared memory pool. + /// + private static PolynomItem[] RentArray(int count) + { + return System.Buffers.ArrayPool.Shared.Rent(count); + } + + /// + /// Returns memory allocated for the polynomial terms back to the shared memory pool. + /// + private static void ReturnArray(PolynomItem[] array) + { + System.Buffers.ArrayPool.Shared.Return(array); + } +#else + // Implement a poor-man's array pool for .NET Framework + [ThreadStatic] + private static List _arrayPool; + + /// + /// Rents memory for the polynomial terms from a shared memory pool. + /// + private static PolynomItem[] RentArray(int count) + { + if (count <= 0) + ThrowArgumentOutOfRangeException(); + + // Search for a suitable array in the thread-local pool, if it has been initialized + if (_arrayPool != null) + { + for (int i = 0; i < _arrayPool.Count; i++) + { + var array = _arrayPool[i]; + if (array.Length >= count) + { + _arrayPool.RemoveAt(i); + return array; + } + } + } + + // No suitable buffer found; create a new one + return new PolynomItem[count]; + + void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number."); + } + } + + /// + /// Returns memory allocated for the polynomial terms back to a shared memory pool. + /// + private static void ReturnArray(PolynomItem[] array) + { + if (array == null) + ThrowArgumentNullException(); + + // Initialize the thread-local pool if it's not already done + if (_arrayPool == null) + _arrayPool = new List(8); + + // Add the buffer back to the pool + _arrayPool.Add(array); + + void ThrowArgumentNullException() + { + throw new ArgumentNullException(nameof(array)); + } + } +#endif + + /// + /// Returns an enumerator that iterates through the polynomial terms. + /// + public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this); + + /// + /// Value type enumerator for the struct. + /// + public struct PolynumEnumerator + { + private Polynom _polynom; + private int _index; + + public PolynumEnumerator(Polynom polynom) + { + _polynom = polynom; + _index = -1; + } + + public PolynomItem Current => _polynom[_index]; + + public bool MoveNext() => ++_index < _polynom._length; + } } } } diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 9716017d..36bc74d0 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -222,12 +222,16 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i } } - //Calculate error correction words - var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length); - int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; - AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset); - + // Generate the generator polynomial using the number of ECC words. + List codeWordWithECC; + using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) + { + //Calculate error correction words + codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); + AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length, generatorPolynom); + int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; + AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset, generatorPolynom); + } //Calculate interleaved code word lengths int interleavedLength = 0; @@ -285,13 +289,13 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i ModulePlacer.AddQuietZone(qr); return qr; - void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count) + void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count, Polynom generatorPolynom) { var groupLength = codewordsInGroup * 8; for (var i = 0; i < blocksInGroup; i++) { var bitBlockList = BinaryStringToBitBlockByteList(bitArray2, offset2, groupLength); - var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo); + var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo, generatorPolynom); codeWordWithECC.Add(new CodewordBlock( bitBlockList, eccWordList) @@ -438,71 +442,62 @@ private static BitArray GetVersionString(int version) /// This method applies polynomial division, using the message polynomial and a generator polynomial, /// to compute the remainder which forms the ECC codewords. /// - private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) + private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynomBase) { var eccWords = eccInfo.ECCPerBlock; // Calculate the message polynomial from the bit array data. var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); - // Generate the generator polynomial using the number of ECC words. - var generatorPolynom = CalculateGeneratorPolynom(eccWords); + var generatorPolynom = generatorPolynomBase.Clone(); // Adjust the exponents in the message polynomial to account for ECC length. - for (var i = 0; i < messagePolynom.PolyItems.Count; i++) - messagePolynom.PolyItems[i] = new PolynomItem(messagePolynom.PolyItems[i].Coefficient, - messagePolynom.PolyItems[i].Exponent + eccWords); + for (var i = 0; i < messagePolynom.Count; i++) + messagePolynom[i] = new PolynomItem(messagePolynom[i].Coefficient, + messagePolynom[i].Exponent + eccWords); // Adjust the generator polynomial exponents based on the message polynomial. - for (var i = 0; i < generatorPolynom.PolyItems.Count; i++) - generatorPolynom.PolyItems[i] = new PolynomItem(generatorPolynom.PolyItems[i].Coefficient, - generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count - 1)); + for (var i = 0; i < generatorPolynom.Count; i++) + generatorPolynom[i] = new PolynomItem(generatorPolynom[i].Coefficient, + generatorPolynom[i].Exponent + (messagePolynom.Count - 1)); // Divide the message polynomial by the generator polynomial to find the remainder. var leadTermSource = messagePolynom; - for (var i = 0; (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0); i++) + for (var i = 0; (leadTermSource.Count > 0 && leadTermSource[leadTermSource.Count - 1].Exponent > 0); i++) { - if (leadTermSource.PolyItems[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. + if (leadTermSource[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. { - leadTermSource.PolyItems.RemoveAt(0); - leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); + leadTermSource.RemoveAt(0); + leadTermSource.Add(new PolynomItem(0, leadTermSource[leadTermSource.Count - 1].Exponent - 1)); } else // Otherwise, perform polynomial reduction using XOR and multiplication with the generator polynomial. { - var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); + // Convert the first coefficient to its corresponding alpha exponent unless it's zero. + // Coefficients that are zero remain zero because log(0) is undefined. + var index0Coefficient = leadTermSource[0].Coefficient; + index0Coefficient = index0Coefficient == 0 ? 0 : GetAlphaExpFromIntVal(index0Coefficient); + var alphaNotation = new PolynomItem(index0Coefficient, leadTermSource[0].Exponent); + var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, alphaNotation, i); ConvertToDecNotationInPlace(resPoly); - resPoly = XORPolynoms(leadTermSource, resPoly); - leadTermSource = resPoly; + var newPoly = XORPolynoms(leadTermSource, resPoly); + // Free memory used by the previous polynomials. + resPoly.Dispose(); + leadTermSource.Dispose(); + // Update the message polynomial with the new remainder. + leadTermSource = newPoly; } } - // Convert the resulting polynomial into a byte array representing the ECC codewords. - var ret = new byte[leadTermSource.PolyItems.Count]; - for (var i = 0; i < leadTermSource.PolyItems.Count; i++) - ret[i] = (byte)leadTermSource.PolyItems[i].Coefficient; - return ret; - } + // Free memory used by the generator polynomial. + generatorPolynom.Dispose(); - /// - /// Converts the coefficients of a polynomial from integer values to their corresponding alpha exponent notation - /// based on a Galois field mapping. This is typically used in error correction calculations where - /// operations are performed on exponents rather than coefficients directly. - /// - private static Polynom ConvertToAlphaNotation(Polynom poly) - { - var newPoly = new Polynom(poly.PolyItems.Count); + // Convert the resulting polynomial into a byte array representing the ECC codewords. + var ret = new byte[leadTermSource.Count]; + for (var i = 0; i < leadTermSource.Count; i++) + ret[i] = (byte)leadTermSource[i].Coefficient; - for (var i = 0; i < poly.PolyItems.Count; i++) - { - // Convert each coefficient to its corresponding alpha exponent unless it's zero. - // Coefficients that are zero remain zero because log(0) is undefined. - newPoly.PolyItems.Add( - new PolynomItem( - (poly.PolyItems[i].Coefficient != 0 - ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) - : 0), - poly.PolyItems[i].Exponent)); // The exponent remains unchanged. - } + // Free memory used by the message polynomial. + leadTermSource.Dispose(); - return newPoly; + return ret; } /// @@ -511,10 +506,10 @@ private static Polynom ConvertToAlphaNotation(Polynom poly) /// private static void ConvertToDecNotationInPlace(Polynom poly) { - for (var i = 0; i < poly.PolyItems.Count; i++) + for (var i = 0; i < poly.Count; i++) { // Convert the alpha exponent of the coefficient to its decimal value and create a new polynomial item with the updated coefficient. - poly.PolyItems[i] = new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent); + poly[i] = new PolynomItem(GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); } } @@ -587,7 +582,7 @@ private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, in var messagePol = new Polynom(count /= 8); for (var i = count - 1; i >= 0; i--) { - messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); + messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); offset += 8; } return messagePol; @@ -601,19 +596,23 @@ private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, in private static Polynom CalculateGeneratorPolynom(int numEccWords) { var generatorPolynom = new Polynom(2); // Start with the simplest form of the polynomial - generatorPolynom.PolyItems.Add(new PolynomItem(0, 1)); - generatorPolynom.PolyItems.Add(new PolynomItem(0, 0)); + generatorPolynom.Add(new PolynomItem(0, 1)); + generatorPolynom.Add(new PolynomItem(0, 0)); - var multiplierPolynom = new Polynom(numEccWords * 2); // Used for polynomial multiplication - for (var i = 1; i <= numEccWords - 1; i++) + using (var multiplierPolynom = new Polynom(numEccWords * 2)) // Used for polynomial multiplication { - // Clear and set up the multiplier polynomial for the current multiplication - multiplierPolynom.PolyItems.Clear(); - multiplierPolynom.PolyItems.Add(new PolynomItem(0, 1)); - multiplierPolynom.PolyItems.Add(new PolynomItem(i, 0)); - - // Multiply the generator polynomial by the current multiplier polynomial - generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + for (var i = 1; i <= numEccWords - 1; i++) + { + // Clear and set up the multiplier polynomial for the current multiplication + multiplierPolynom.Clear(); + multiplierPolynom.Add(new PolynomItem(0, 1)); + multiplierPolynom.Add(new PolynomItem(i, 0)); + + // Multiply the generator polynomial by the current multiplier polynomial + var newGeneratorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + generatorPolynom.Dispose(); + generatorPolynom = newGeneratorPolynom; + } } return generatorPolynom; // Return the completed generator polynomial @@ -956,9 +955,9 @@ private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) { // Determine the larger of the two polynomials to guide the XOR operation. - var resultPolynom = new Polynom(Math.Max(messagePolynom.PolyItems.Count, resPolynom.PolyItems.Count)); + var resultPolynom = new Polynom(Math.Max(messagePolynom.Count, resPolynom.Count) - 1); Polynom longPoly, shortPoly; - if (messagePolynom.PolyItems.Count >= resPolynom.PolyItems.Count) + if (messagePolynom.Count >= resPolynom.Count) { longPoly = messagePolynom; shortPoly = resPolynom; @@ -970,16 +969,16 @@ private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) } // XOR the coefficients of the two polynomials. - for (var i = 0; i < longPoly.PolyItems.Count; i++) + for (var i = 1; i < longPoly.Count; i++) { var polItemRes = new PolynomItem( - longPoly.PolyItems[i].Coefficient ^ - (shortPoly.PolyItems.Count > i ? shortPoly.PolyItems[i].Coefficient : 0), - messagePolynom.PolyItems[0].Exponent - i + longPoly[i].Coefficient ^ + (shortPoly.Count > i ? shortPoly[i].Coefficient : 0), + messagePolynom[0].Exponent - i ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } - resultPolynom.PolyItems.RemoveAt(0); + return resultPolynom; } @@ -989,15 +988,15 @@ private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) /// private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) { - var resultPolynom = new Polynom(genPolynom.PolyItems.Count); - foreach (var polItemBase in genPolynom.PolyItems) + var resultPolynom = new Polynom(genPolynom.Count); + foreach (var polItemBase in genPolynom) { var polItemRes = new PolynomItem( (polItemBase.Coefficient + leadTerm.Coefficient) % 255, polItemBase.Exponent - lowerExponentBy ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } return resultPolynom; } @@ -1011,12 +1010,12 @@ private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, Po private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) { // Initialize a new polynomial with a size based on the product of the sizes of the two input polynomials. - var resultPolynom = new Polynom(polynomMultiplier.PolyItems.Count * polynomBase.PolyItems.Count); + var resultPolynom = new Polynom(polynomMultiplier.Count * polynomBase.Count); // Multiply each term of the first polynomial by each term of the second polynomial. - foreach (var polItemBase in polynomMultiplier.PolyItems) + foreach (var polItemBase in polynomMultiplier) { - foreach (var polItemMulti in polynomBase.PolyItems) + foreach (var polItemMulti in polynomBase) { // Create a new polynomial term with the coefficients added (as exponents) and exponents summed. var polItemRes = new PolynomItem @@ -1024,18 +1023,18 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), (polItemBase.Exponent + polItemMulti.Exponent) ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } } // Identify and merge terms with the same exponent. - var toGlue = GetNotUniqueExponents(resultPolynom.PolyItems); + var toGlue = GetNotUniqueExponents(resultPolynom); var gluedPolynoms = new PolynomItem[toGlue.Length]; var gluedPolynomsIndex = 0; foreach (var exponent in toGlue) { var coefficient = 0; - foreach (var polynomOld in resultPolynom.PolyItems) + foreach (var polynomOld in resultPolynom) { if (polynomOld.Exponent == exponent) coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); @@ -1047,18 +1046,18 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno } // Remove duplicated exponents and add the corrected ones back. - for (int i = resultPolynom.PolyItems.Count - 1; i >= 0; i--) - if (toGlue.Contains(resultPolynom.PolyItems[i].Exponent)) - resultPolynom.PolyItems.RemoveAt(i); + for (int i = resultPolynom.Count - 1; i >= 0; i--) + if (toGlue.Contains(resultPolynom[i].Exponent)) + resultPolynom.RemoveAt(i); foreach (var polynom in gluedPolynoms) - resultPolynom.PolyItems.Add(polynom); + resultPolynom.Add(polynom); // Sort the polynomial terms by exponent in descending order. - resultPolynom.PolyItems.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); + resultPolynom.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); return resultPolynom; // Auxiliary function to identify exponents that appear more than once in the polynomial. - int[] GetNotUniqueExponents(List list) + int[] GetNotUniqueExponents(Polynom list) { var dic = new Dictionary(list.Count); foreach (var row in list) diff --git a/QRCoderBenchmarks/QRCodeGenerator.cs b/QRCoderBenchmarks/QRCodeGenerator.cs index f05ba6d4..742d5be5 100644 --- a/QRCoderBenchmarks/QRCodeGenerator.cs +++ b/QRCoderBenchmarks/QRCodeGenerator.cs @@ -20,4 +20,12 @@ public void CreateQRCodeLong() QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); _ = qrGenerator.CreateQrCode(payload, QRCoder.QRCodeGenerator.ECCLevel.H); } + + [Benchmark] + public void CreateQRCodeLongest() + { + var str = new string('a', 2600); + QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); + _ = qrGenerator.CreateQrCode(str, QRCoder.QRCodeGenerator.ECCLevel.L); + } }