Toolkit.UI.Controls.Avalonia
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using Gma.QrCodeNet.Encoding.DataEncodation;
|
||||
|
||||
namespace Gma.QrCodeNet.Encoding.Versions;
|
||||
|
||||
internal static class VersionControl
|
||||
{
|
||||
private const int NumBitsModeIndicator = 4;
|
||||
private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding;
|
||||
|
||||
private static readonly int[] VERSION_GROUP = new int[] { 9, 26, 40 };
|
||||
|
||||
/// <summary>
|
||||
/// Determine which version to use
|
||||
/// </summary>
|
||||
/// <param name="dataBitsLength">Number of bits for encoded content</param>
|
||||
/// <param name="encodingName">Encoding name for EightBitByte</param>
|
||||
/// <returns>VersionDetail and ECI</returns>
|
||||
internal static VersionControlStruct InitialSetup(int dataBitsLength, ErrorCorrectionLevel level, string encodingName)
|
||||
{
|
||||
int totalDataBits = dataBitsLength;
|
||||
|
||||
bool containECI = false;
|
||||
|
||||
BitList eciHeader = new();
|
||||
|
||||
if (encodingName is not DefaultEncoding and not QRCodeConstantVariable.UTF8Encoding)
|
||||
{
|
||||
ECISet eciSet = new(ECISet.AppendOption.NameToValue);
|
||||
int eciValue = eciSet.GetECIValueByName(encodingName);
|
||||
|
||||
totalDataBits += ECISet.NumOfECIHeaderBits(eciValue);
|
||||
eciHeader = eciSet.GetECIHeader(encodingName);
|
||||
containECI = true;
|
||||
}
|
||||
|
||||
// Determine which version group it belong to
|
||||
int searchGroup = DynamicSearchIndicator(totalDataBits, level);
|
||||
|
||||
int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet();
|
||||
|
||||
totalDataBits += (NumBitsModeIndicator + charCountIndicator[searchGroup]);
|
||||
|
||||
int lowerSearchBoundary = searchGroup == 0 ? 1 : (VERSION_GROUP[searchGroup - 1] + 1);
|
||||
int higherSearchBoundary = VERSION_GROUP[searchGroup];
|
||||
|
||||
// Binary search to find proper version
|
||||
int versionNum = BinarySearch(totalDataBits, level, lowerSearchBoundary, higherSearchBoundary);
|
||||
|
||||
VersionControlStruct vcStruct = FillVCStruct(versionNum, level);
|
||||
|
||||
vcStruct.IsContainECI = containECI;
|
||||
|
||||
vcStruct.ECIHeader = eciHeader;
|
||||
|
||||
return vcStruct;
|
||||
}
|
||||
|
||||
private static VersionControlStruct FillVCStruct(int versionNum, ErrorCorrectionLevel level)
|
||||
{
|
||||
if (versionNum is < 1 or > 40)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected version number: {versionNum}");
|
||||
}
|
||||
|
||||
VersionControlStruct vcStruct = new();
|
||||
|
||||
int version = versionNum;
|
||||
|
||||
QRCodeVersion versionData = VersionTable.GetVersionByNum(versionNum);
|
||||
|
||||
int numTotalBytes = versionData.TotalCodewords;
|
||||
|
||||
ErrorCorrectionBlocks ecBlocks = versionData.GetECBlocksByLevel(level);
|
||||
int numDataBytes = numTotalBytes - ecBlocks.NumErrorCorrectionCodewards;
|
||||
int numECBlocks = ecBlocks.NumBlocks;
|
||||
|
||||
VersionDetail vcDetail = new(version, numTotalBytes, numDataBytes, numECBlocks);
|
||||
|
||||
vcStruct.VersionDetail = vcDetail;
|
||||
return vcStruct;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide which version group it belong to
|
||||
/// </summary>
|
||||
/// <param name="numBits">Number of bits for bitlist where it contain DataBits encode from input content and ECI header</param>
|
||||
/// <param name="level">Error correction level</param>
|
||||
/// <returns>Version group index for VERSION_GROUP</returns>
|
||||
private static int DynamicSearchIndicator(int numBits, ErrorCorrectionLevel level)
|
||||
{
|
||||
int[] charCountIndicator = CharCountIndicatorTable.GetCharCountIndicatorSet();
|
||||
int loopLength = VERSION_GROUP.Length;
|
||||
for (int i = 0; i < loopLength; i++)
|
||||
{
|
||||
int totalBits = numBits + NumBitsModeIndicator + charCountIndicator[i];
|
||||
|
||||
QRCodeVersion version = VersionTable.GetVersionByNum(VERSION_GROUP[i]);
|
||||
int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards;
|
||||
|
||||
int dataCodewords = version.TotalCodewords - numECCodewords;
|
||||
|
||||
if (totalBits <= dataCodewords * 8)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InputOutOfBoundaryException($"QRCode do not have enough space for {(numBits + NumBitsModeIndicator + charCountIndicator[2])} bits");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use number of data bits(header + eci header + data bits from EncoderBase) to search for proper version to use
|
||||
/// between min and max boundary.
|
||||
/// Boundary define by DynamicSearchIndicator method.
|
||||
/// </summary>
|
||||
private static int BinarySearch(int numDataBits, ErrorCorrectionLevel level, int lowerVersionNum, int higherVersionNum)
|
||||
{
|
||||
int middleVersionNumber;
|
||||
|
||||
while (lowerVersionNum <= higherVersionNum)
|
||||
{
|
||||
middleVersionNumber = (lowerVersionNum + higherVersionNum) / 2;
|
||||
QRCodeVersion version = VersionTable.GetVersionByNum(middleVersionNumber);
|
||||
int numECCodewords = version.GetECBlocksByLevel(level).NumErrorCorrectionCodewards;
|
||||
int dataCodewords = version.TotalCodewords - numECCodewords;
|
||||
|
||||
if (dataCodewords << 3 == numDataBits)
|
||||
{
|
||||
return middleVersionNumber;
|
||||
}
|
||||
|
||||
if (dataCodewords << 3 > numDataBits)
|
||||
{
|
||||
higherVersionNum = middleVersionNumber - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lowerVersionNum = middleVersionNumber + 1;
|
||||
}
|
||||
}
|
||||
return lowerVersionNum;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user