// Returns a new list of segments based on the given text and modes, such that // consecutive code points in the same mode are put into the same segment. private static List <QrSegment> SplitIntoSegments(int[] codePoints, QrSegment.Mode[] charModes) { if (codePoints.Length == 0) { throw new ArgumentOutOfRangeException((codePoints.GetType().Name)); } List <QrSegment> result = new List <QrSegment>(); // Accumulate run of modes QrSegment.Mode curMode = charModes[0]; int start = 0; for (int i = 1;; i++) { if (i < codePoints.Length && charModes[i] == curMode) { continue; } string s = FromCodePoints(codePoints, start, i - start); if (curMode == QrSegment.Mode.Byte) { result.Add(QrSegment.MakeBytes(Encoding.UTF8.GetBytes(s))); } else if (curMode == QrSegment.Mode.Numeric) { result.Add(QrSegment.MakeNumeric(s)); } else if (curMode == QrSegment.Mode.Alphanumeric) { result.Add(QrSegment.MakeAlphanumeric(s)); } else if (curMode == QrSegment.Mode.Kanji) { result.Add(MakeKanji(s)); } else { Debug.Assert(false); } if (i >= codePoints.Length) { return(result); } curMode = charModes[i]; start = i; } }
// Returns a new array representing the optimal mode per code point based on the given text and version. private static QrSegment.Mode[] ComputeCharacterModes(int[] codePoints, int version) { if (codePoints.Length == 0) { throw new ArgumentOutOfRangeException(nameof(codePoints)); } QrSegment.Mode[] modeTypes = { QrSegment.Mode.Byte, QrSegment.Mode.Alphanumeric, QrSegment.Mode.Numeric, QrSegment.Mode.Kanji }; // Do not modify int numModes = modeTypes.Length; // Segment header sizes, measured in 1/6 bits int[] headCosts = new int[numModes]; for (int i = 0; i < numModes; i++) { headCosts[i] = (4 + modeTypes[i].NumCharCountBits(version)) * 6; } // charModes[i][j] represents the mode to encode the code point at // index i such that the final segment ends in modeTypes[j] and the // total number of bits is minimized over all possible choices QrSegment.Mode[,] charModes = new QrSegment.Mode[codePoints.Length, numModes]; // At the beginning of each iteration of the loop below, // prevCosts[j] is the exact minimum number of 1/6 bits needed to // encode the entire string prefix of length i, and end in modeTypes[j] int[] prevCosts = (int[])headCosts.Clone(); // Calculate costs using dynamic programming for (int i = 0; i < codePoints.Length; i++) { int c = codePoints[i]; int[] curCosts = new int[numModes]; { // Always extend a byte mode segment curCosts[0] = prevCosts[0] + CountUtf8Bytes(c) * 8 * 6; charModes[i, 0] = modeTypes[0]; } // Extend a segment if possible if (QrSegment.AlphanumericCharset.IndexOf((char)c) != -1) { // Is alphanumeric curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char charModes[i, 1] = modeTypes[1]; } if ('0' <= c && c <= '9') { // Is numeric curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit charModes[i, 2] = modeTypes[2]; } if (IsKanji(c)) { curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char charModes[i, 3] = modeTypes[3]; } // Start new segment at the end to switch modes for (int j = 0; j < numModes; j++) { // To mode for (int k = 0; k < numModes; k++) { // From mode int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]; if (charModes[i, k] == null || charModes[i, j] != null && newCost >= curCosts[j]) { continue; } curCosts[j] = newCost; charModes[i, j] = modeTypes[k]; } } prevCosts = curCosts; } // Find optimal ending mode QrSegment.Mode curMode = null; for (int i = 0, minCost = 0; i < numModes; i++) { if (curMode != null && prevCosts[i] >= minCost) { continue; } minCost = prevCosts[i]; curMode = modeTypes[i]; } // Get optimal mode for each code point by tracing backwards QrSegment.Mode[] result = new QrSegment.Mode[codePoints.Length]; for (int i = result.Length - 1; i >= 0; i--) { for (int j = 0; j < numModes; j++) { if (modeTypes[j] != curMode) { continue; } curMode = charModes[i, j]; result[i] = curMode; break; } } return(result); }