/// Decodes Code 128 barcodes.
///
/// Sean Owen
///
public sealed class Code128Reader : OneDReader
{
internal static int[][] CODE_PATTERNS = {
new[] {2, 1, 2, 2, 2, 2}, // 0
new[] {2, 2, 2, 1, 2, 2},
new[] {2, 2, 2, 2, 2, 1},
new[] {1, 2, 1, 2, 2, 3},
new[] {1, 2, 1, 3, 2, 2},
new[] {1, 3, 1, 2, 2, 2}, // 5
new[] {1, 2, 2, 2, 1, 3},
new[] {1, 2, 2, 3, 1, 2},
new[] {1, 3, 2, 2, 1, 2},
new[] {2, 2, 1, 2, 1, 3},
new[] {2, 2, 1, 3, 1, 2}, // 10
new[] {2, 3, 1, 2, 1, 2},
new[] {1, 1, 2, 2, 3, 2},
new[] {1, 2, 2, 1, 3, 2},
new[] {1, 2, 2, 2, 3, 1},
new[] {1, 1, 3, 2, 2, 2}, // 15
new[] {1, 2, 3, 1, 2, 2},
new[] {1, 2, 3, 2, 2, 1},
new[] {2, 2, 3, 2, 1, 1},
new[] {2, 2, 1, 1, 3, 2},
new[] {2, 2, 1, 2, 3, 1}, // 20
new[] {2, 1, 3, 2, 1, 2},
new[] {2, 2, 3, 1, 1, 2},
new[] {3, 1, 2, 1, 3, 1},
new[] {3, 1, 1, 2, 2, 2},
new[] {3, 2, 1, 1, 2, 2}, // 25
new[] {3, 2, 1, 2, 2, 1},
new[] {3, 1, 2, 2, 1, 2},
new[] {3, 2, 2, 1, 1, 2},
new[] {3, 2, 2, 2, 1, 1},
new[] {2, 1, 2, 1, 2, 3}, // 30
new[] {2, 1, 2, 3, 2, 1},
new[] {2, 3, 2, 1, 2, 1},
new[] {1, 1, 1, 3, 2, 3},
new[] {1, 3, 1, 1, 2, 3},
new[] {1, 3, 1, 3, 2, 1}, // 35
new[] {1, 1, 2, 3, 1, 3},
new[] {1, 3, 2, 1, 1, 3},
new[] {1, 3, 2, 3, 1, 1},
new[] {2, 1, 1, 3, 1, 3},
new[] {2, 3, 1, 1, 1, 3}, // 40
new[] {2, 3, 1, 3, 1, 1},
new[] {1, 1, 2, 1, 3, 3},
new[] {1, 1, 2, 3, 3, 1},
new[] {1, 3, 2, 1, 3, 1},
new[] {1, 1, 3, 1, 2, 3}, // 45
new[] {1, 1, 3, 3, 2, 1},
new[] {1, 3, 3, 1, 2, 1},
new[] {3, 1, 3, 1, 2, 1},
new[] {2, 1, 1, 3, 3, 1},
new[] {2, 3, 1, 1, 3, 1}, // 50
new[] {2, 1, 3, 1, 1, 3},
new[] {2, 1, 3, 3, 1, 1},
new[] {2, 1, 3, 1, 3, 1},
new[] {3, 1, 1, 1, 2, 3},
new[] {3, 1, 1, 3, 2, 1}, // 55
new[] {3, 3, 1, 1, 2, 1},
new[] {3, 1, 2, 1, 1, 3},
new[] {3, 1, 2, 3, 1, 1},
new[] {3, 3, 2, 1, 1, 1},
new[] {3, 1, 4, 1, 1, 1}, // 60
new[] {2, 2, 1, 4, 1, 1},
new[] {4, 3, 1, 1, 1, 1},
new[] {1, 1, 1, 2, 2, 4},
new[] {1, 1, 1, 4, 2, 2},
new[] {1, 2, 1, 1, 2, 4}, // 65
new[] {1, 2, 1, 4, 2, 1},
new[] {1, 4, 1, 1, 2, 2},
new[] {1, 4, 1, 2, 2, 1},
new[] {1, 1, 2, 2, 1, 4},
new[] {1, 1, 2, 4, 1, 2}, // 70
new[] {1, 2, 2, 1, 1, 4},
new[] {1, 2, 2, 4, 1, 1},
new[] {1, 4, 2, 1, 1, 2},
new[] {1, 4, 2, 2, 1, 1},
new[] {2, 4, 1, 2, 1, 1}, // 75
new[] {2, 2, 1, 1, 1, 4},
new[] {4, 1, 3, 1, 1, 1},
new[] {2, 4, 1, 1, 1, 2},
new[] {1, 3, 4, 1, 1, 1},
new[] {1, 1, 1, 2, 4, 2}, // 80
new[] {1, 2, 1, 1, 4, 2},
new[] {1, 2, 1, 2, 4, 1},
new[] {1, 1, 4, 2, 1, 2},
new[] {1, 2, 4, 1, 1, 2},
new[] {1, 2, 4, 2, 1, 1}, // 85
new[] {4, 1, 1, 2, 1, 2},
new[] {4, 2, 1, 1, 1, 2},
new[] {4, 2, 1, 2, 1, 1},
new[] {2, 1, 2, 1, 4, 1},
new[] {2, 1, 4, 1, 2, 1}, // 90
new[] {4, 1, 2, 1, 2, 1},
new[] {1, 1, 1, 1, 4, 3},
new[] {1, 1, 1, 3, 4, 1},
new[] {1, 3, 1, 1, 4, 1},
new[] {1, 1, 4, 1, 1, 3}, // 95
new[] {1, 1, 4, 3, 1, 1},
new[] {4, 1, 1, 1, 1, 3},
new[] {4, 1, 1, 3, 1, 1},
new[] {1, 1, 3, 1, 4, 1},
new[] {1, 1, 4, 1, 3, 1}, // 100
new[] {3, 1, 1, 1, 4, 1},
new[] {4, 1, 1, 1, 3, 1},
new[] {2, 1, 1, 4, 1, 2},
new[] {2, 1, 1, 2, 1, 4},
new[] {2, 1, 1, 2, 3, 2}, // 105
new[] {2, 3, 3, 1, 1, 1, 2}
};
private static readonly int MAX_AVG_VARIANCE = (int)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);
private static readonly int MAX_INDIVIDUAL_VARIANCE = (int)(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
private const int CODE_SHIFT = 98;
private const int CODE_CODE_C = 99;
private const int CODE_CODE_B = 100;
private const int CODE_CODE_A = 101;
private const int CODE_FNC_1 = 102;
private const int CODE_FNC_2 = 97;
private const int CODE_FNC_3 = 96;
private const int CODE_FNC_4_A = 101;
private const int CODE_FNC_4_B = 100;
private const int CODE_START_A = 103;
private const int CODE_START_B = 104;
private const int CODE_START_C = 105;
private const int CODE_STOP = 106;
private static int[] findStartPattern(BitArray row)
{
int width = row.Size;
int rowOffset = row.getNextSet(0);
int counterPosition = 0;
int[] counters = new int[6];
int patternStart = rowOffset;
bool isWhite = false;
int patternLength = counters.Length;
for (int i = rowOffset; i < width; i++)
{
if (row[i] ^ isWhite)
{
counters[counterPosition]++;
}
else
{
if (counterPosition == patternLength - 1)
{
int bestVariance = MAX_AVG_VARIANCE;
int bestMatch = -1;
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++)
{
int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],
MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance)
{
bestVariance = variance;
bestMatch = startCode;
}
}
if (bestMatch >= 0)
{
// Look for whitespace before start pattern, >= 50% of width of start pattern
if (row.isRange(Math.Max(0, patternStart - (i - patternStart) / 2), patternStart,
false))
{
return new int[] { patternStart, i, bestMatch };
}
}
patternStart += counters[0] + counters[1];
Array.Copy(counters, 2, counters, 0, patternLength - 2);
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
}
else
{
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
return null;
}
private static bool decodeCode(BitArray row, int[] counters, int rowOffset, out int code)
{
code = -1;
if (!recordPattern(row, rowOffset, counters))
return false;
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
for (int d = 0; d < CODE_PATTERNS.Length; d++)
{
int[] pattern = CODE_PATTERNS[d];
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance)
{
bestVariance = variance;
code = d;
}
}
// TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
return code >= 0;
}
override public Result decodeRow(int rowNumber, BitArray row, IDictionary