mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-17 06:36:36 +08:00
589 lines
18 KiB
C#
589 lines
18 KiB
C#
/*
|
|
* Copyright 2009 ZXing authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using ZXing.Common;
|
|
|
|
namespace ZXing.OneD.RSS
|
|
{
|
|
/// <summary>
|
|
/// Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.
|
|
/// </summary>
|
|
public sealed class RSS14Reader : AbstractRSSReader
|
|
{
|
|
private static readonly int[] OUTSIDE_EVEN_TOTAL_SUBSET = { 1, 10, 34, 70, 126 };
|
|
private static readonly int[] INSIDE_ODD_TOTAL_SUBSET = { 4, 20, 48, 81 };
|
|
private static readonly int[] OUTSIDE_GSUM = { 0, 161, 961, 2015, 2715 };
|
|
private static readonly int[] INSIDE_GSUM = { 0, 336, 1036, 1516 };
|
|
private static readonly int[] OUTSIDE_ODD_WIDEST = { 8, 6, 4, 3, 1 };
|
|
private static readonly int[] INSIDE_ODD_WIDEST = { 2, 4, 6, 8 };
|
|
|
|
private static readonly int[][] FINDER_PATTERNS = {
|
|
new[] {3, 8, 2, 1},
|
|
new[] {3, 5, 5, 1},
|
|
new[] {3, 3, 7, 1},
|
|
new[] {3, 1, 9, 1},
|
|
new[] {2, 7, 4, 1},
|
|
new[] {2, 5, 6, 1},
|
|
new[] {2, 3, 8, 1},
|
|
new[] {1, 5, 7, 1},
|
|
new[] {1, 3, 9, 1},
|
|
};
|
|
|
|
private readonly List<Pair> possibleLeftPairs;
|
|
private readonly List<Pair> possibleRightPairs;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RSS14Reader"/> class.
|
|
/// </summary>
|
|
public RSS14Reader()
|
|
{
|
|
possibleLeftPairs = new List<Pair>();
|
|
possibleRightPairs = new List<Pair>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// <p>Attempts to decode a one-dimensional barcode format given a single row of
|
|
/// an image.</p>
|
|
/// </summary>
|
|
/// <param name="rowNumber">row number from top of the row</param>
|
|
/// <param name="row">the black/white pixel data of the row</param>
|
|
/// <param name="hints">decode hints</param>
|
|
/// <returns>
|
|
/// <see cref="Result"/>containing encoded string and start/end of barcode or null, if an error occurs or barcode cannot be found
|
|
/// </returns>
|
|
override public Result decodeRow(int rowNumber,
|
|
BitArray row,
|
|
IDictionary<DecodeHintType, object> hints)
|
|
{
|
|
Pair leftPair = decodePair(row, false, rowNumber, hints);
|
|
addOrTally(possibleLeftPairs, leftPair);
|
|
row.reverse();
|
|
Pair rightPair = decodePair(row, true, rowNumber, hints);
|
|
addOrTally(possibleRightPairs, rightPair);
|
|
row.reverse();
|
|
int lefSize = possibleLeftPairs.Count;
|
|
for (int i = 0; i < lefSize; i++)
|
|
{
|
|
Pair left = possibleLeftPairs[i];
|
|
if (left.Count > 1)
|
|
{
|
|
int rightSize = possibleRightPairs.Count;
|
|
for (int j = 0; j < rightSize; j++)
|
|
{
|
|
Pair right = possibleRightPairs[j];
|
|
if (right.Count > 1)
|
|
{
|
|
if (checkChecksum(left, right))
|
|
{
|
|
return constructResult(left, right);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static void addOrTally(IList<Pair> possiblePairs, Pair pair)
|
|
{
|
|
if (pair == null)
|
|
{
|
|
return;
|
|
}
|
|
bool found = false;
|
|
foreach (Pair other in possiblePairs)
|
|
{
|
|
if (other.Value == pair.Value)
|
|
{
|
|
other.incrementCount();
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
possiblePairs.Add(pair);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets this instance.
|
|
/// </summary>
|
|
public override void reset()
|
|
{
|
|
possibleLeftPairs.Clear();
|
|
possibleRightPairs.Clear();
|
|
}
|
|
|
|
private static Result constructResult(Pair leftPair, Pair rightPair)
|
|
{
|
|
long symbolValue = 4537077L * leftPair.Value + rightPair.Value;
|
|
String text = symbolValue.ToString();
|
|
|
|
StringBuilder buffer = new StringBuilder(14);
|
|
for (int i = 13 - text.Length; i > 0; i--)
|
|
{
|
|
buffer.Append('0');
|
|
}
|
|
buffer.Append(text);
|
|
|
|
int checkDigit = 0;
|
|
for (int i = 0; i < 13; i++)
|
|
{
|
|
int digit = buffer[i] - '0';
|
|
checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;
|
|
}
|
|
checkDigit = 10 - (checkDigit % 10);
|
|
if (checkDigit == 10)
|
|
{
|
|
checkDigit = 0;
|
|
}
|
|
buffer.Append(checkDigit);
|
|
|
|
ResultPoint[] leftPoints = leftPair.FinderPattern.ResultPoints;
|
|
ResultPoint[] rightPoints = rightPair.FinderPattern.ResultPoints;
|
|
return new Result(
|
|
buffer.ToString(),
|
|
null,
|
|
new ResultPoint[] { leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1], },
|
|
BarcodeFormat.RSS_14);
|
|
}
|
|
|
|
private static bool checkChecksum(Pair leftPair, Pair rightPair)
|
|
{
|
|
//int leftFPValue = leftPair.FinderPattern.Value;
|
|
//int rightFPValue = rightPair.FinderPattern.Value;
|
|
//if ((leftFPValue == 0 && rightFPValue == 8) ||
|
|
// (leftFPValue == 8 && rightFPValue == 0))
|
|
//{
|
|
//}
|
|
int checkValue = (leftPair.ChecksumPortion + 16 * rightPair.ChecksumPortion) % 79;
|
|
int targetCheckValue =
|
|
9 * leftPair.FinderPattern.Value + rightPair.FinderPattern.Value;
|
|
if (targetCheckValue > 72)
|
|
{
|
|
targetCheckValue--;
|
|
}
|
|
if (targetCheckValue > 8)
|
|
{
|
|
targetCheckValue--;
|
|
}
|
|
return checkValue == targetCheckValue;
|
|
}
|
|
|
|
private Pair decodePair(BitArray row, bool right, int rowNumber, IDictionary<DecodeHintType, object> hints)
|
|
{
|
|
int[] startEnd = findFinderPattern(row, 0, right);
|
|
if (startEnd == null)
|
|
return null;
|
|
FinderPattern pattern = parseFoundFinderPattern(row, rowNumber, right, startEnd);
|
|
if (pattern == null)
|
|
return null;
|
|
|
|
ResultPointCallback resultPointCallback = hints == null || !hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK) ? null :
|
|
(ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK];
|
|
|
|
if (resultPointCallback != null)
|
|
{
|
|
float center = (startEnd[0] + startEnd[1]) / 2.0f;
|
|
if (right)
|
|
{
|
|
// row is actually reversed
|
|
center = row.Size - 1 - center;
|
|
}
|
|
resultPointCallback(new ResultPoint(center, rowNumber));
|
|
}
|
|
|
|
DataCharacter outside = decodeDataCharacter(row, pattern, true);
|
|
if (outside == null)
|
|
return null;
|
|
DataCharacter inside = decodeDataCharacter(row, pattern, false);
|
|
if (inside == null)
|
|
return null;
|
|
return new Pair(1597*outside.Value + inside.Value,
|
|
outside.ChecksumPortion + 4*inside.ChecksumPortion,
|
|
pattern);
|
|
}
|
|
|
|
private DataCharacter decodeDataCharacter(BitArray row, FinderPattern pattern, bool outsideChar)
|
|
{
|
|
int[] counters = getDataCharacterCounters();
|
|
counters[0] = 0;
|
|
counters[1] = 0;
|
|
counters[2] = 0;
|
|
counters[3] = 0;
|
|
counters[4] = 0;
|
|
counters[5] = 0;
|
|
counters[6] = 0;
|
|
counters[7] = 0;
|
|
|
|
if (outsideChar)
|
|
{
|
|
recordPatternInReverse(row, pattern.StartEnd[0], counters);
|
|
}
|
|
else
|
|
{
|
|
recordPattern(row, pattern.StartEnd[1] + 1, counters);
|
|
// reverse it
|
|
for (int i = 0, j = counters.Length - 1; i < j; i++, j--)
|
|
{
|
|
int temp = counters[i];
|
|
counters[i] = counters[j];
|
|
counters[j] = temp;
|
|
}
|
|
}
|
|
|
|
int numModules = outsideChar ? 16 : 15;
|
|
float elementWidth = (float)count(counters) / (float)numModules;
|
|
|
|
int[] oddCounts = this.getOddCounts();
|
|
int[] evenCounts = this.getEvenCounts();
|
|
float[] oddRoundingErrors = this.getOddRoundingErrors();
|
|
float[] evenRoundingErrors = this.getEvenRoundingErrors();
|
|
|
|
for (int i = 0; i < counters.Length; i++)
|
|
{
|
|
float value = (float)counters[i] / elementWidth;
|
|
int rounded = (int)(value + 0.5f); // Round
|
|
if (rounded < 1)
|
|
{
|
|
rounded = 1;
|
|
}
|
|
else if (rounded > 8)
|
|
{
|
|
rounded = 8;
|
|
}
|
|
int offset = i >> 1;
|
|
if ((i & 0x01) == 0)
|
|
{
|
|
oddCounts[offset] = rounded;
|
|
oddRoundingErrors[offset] = value - rounded;
|
|
}
|
|
else
|
|
{
|
|
evenCounts[offset] = rounded;
|
|
evenRoundingErrors[offset] = value - rounded;
|
|
}
|
|
}
|
|
|
|
if (!adjustOddEvenCounts(outsideChar, numModules))
|
|
return null;
|
|
|
|
int oddSum = 0;
|
|
int oddChecksumPortion = 0;
|
|
for (int i = oddCounts.Length - 1; i >= 0; i--)
|
|
{
|
|
oddChecksumPortion *= 9;
|
|
oddChecksumPortion += oddCounts[i];
|
|
oddSum += oddCounts[i];
|
|
}
|
|
int evenChecksumPortion = 0;
|
|
int evenSum = 0;
|
|
for (int i = evenCounts.Length - 1; i >= 0; i--)
|
|
{
|
|
evenChecksumPortion *= 9;
|
|
evenChecksumPortion += evenCounts[i];
|
|
evenSum += evenCounts[i];
|
|
}
|
|
int checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;
|
|
|
|
if (outsideChar)
|
|
{
|
|
if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4)
|
|
{
|
|
return null;
|
|
}
|
|
int group = (12 - oddSum) / 2;
|
|
int oddWidest = OUTSIDE_ODD_WIDEST[group];
|
|
int evenWidest = 9 - oddWidest;
|
|
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, false);
|
|
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, true);
|
|
int tEven = OUTSIDE_EVEN_TOTAL_SUBSET[group];
|
|
int gSum = OUTSIDE_GSUM[group];
|
|
return new DataCharacter(vOdd * tEven + vEven + gSum, checksumPortion);
|
|
}
|
|
else
|
|
{
|
|
if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4)
|
|
{
|
|
return null;
|
|
}
|
|
int group = (10 - evenSum) / 2;
|
|
int oddWidest = INSIDE_ODD_WIDEST[group];
|
|
int evenWidest = 9 - oddWidest;
|
|
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);
|
|
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);
|
|
int tOdd = INSIDE_ODD_TOTAL_SUBSET[group];
|
|
int gSum = INSIDE_GSUM[group];
|
|
return new DataCharacter(vEven * tOdd + vOdd + gSum, checksumPortion);
|
|
}
|
|
}
|
|
|
|
private int[] findFinderPattern(BitArray row, int rowOffset, bool rightFinderPattern)
|
|
{
|
|
|
|
int[] counters = getDecodeFinderCounters();
|
|
counters[0] = 0;
|
|
counters[1] = 0;
|
|
counters[2] = 0;
|
|
counters[3] = 0;
|
|
|
|
int width = row.Size;
|
|
bool isWhite = false;
|
|
while (rowOffset < width)
|
|
{
|
|
isWhite = !row[rowOffset];
|
|
if (rightFinderPattern == isWhite)
|
|
{
|
|
// Will encounter white first when searching for right finder pattern
|
|
break;
|
|
}
|
|
rowOffset++;
|
|
}
|
|
|
|
int counterPosition = 0;
|
|
int patternStart = rowOffset;
|
|
for (int x = rowOffset; x < width; x++)
|
|
{
|
|
if (row[x] ^ isWhite)
|
|
{
|
|
counters[counterPosition]++;
|
|
}
|
|
else
|
|
{
|
|
if (counterPosition == 3)
|
|
{
|
|
if (isFinderPattern(counters))
|
|
{
|
|
return new int[] { patternStart, x };
|
|
}
|
|
patternStart += counters[0] + counters[1];
|
|
counters[0] = counters[2];
|
|
counters[1] = counters[3];
|
|
counters[2] = 0;
|
|
counters[3] = 0;
|
|
counterPosition--;
|
|
}
|
|
else
|
|
{
|
|
counterPosition++;
|
|
}
|
|
counters[counterPosition] = 1;
|
|
isWhite = !isWhite;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, bool right, int[] startEnd)
|
|
{
|
|
// Actually we found elements 2-5
|
|
bool firstIsBlack = row[startEnd[0]];
|
|
int firstElementStart = startEnd[0] - 1;
|
|
// Locate element 1
|
|
while (firstElementStart >= 0 && firstIsBlack ^ row[firstElementStart])
|
|
{
|
|
firstElementStart--;
|
|
}
|
|
firstElementStart++;
|
|
int firstCounter = startEnd[0] - firstElementStart;
|
|
// Make 'counters' hold 1-4
|
|
int[] counters = getDecodeFinderCounters();
|
|
Array.Copy(counters, 0, counters, 1, counters.Length - 1);
|
|
counters[0] = firstCounter;
|
|
int value;
|
|
if (!parseFinderValue(counters, FINDER_PATTERNS, out value))
|
|
return null;
|
|
int start = firstElementStart;
|
|
int end = startEnd[1];
|
|
if (right)
|
|
{
|
|
// row is actually reversed
|
|
start = row.Size - 1 - start;
|
|
end = row.Size - 1 - end;
|
|
}
|
|
return new FinderPattern(value, new int[] { firstElementStart, startEnd[1] }, start, end, rowNumber);
|
|
}
|
|
|
|
private bool adjustOddEvenCounts(bool outsideChar, int numModules)
|
|
{
|
|
int oddSum = count(getOddCounts());
|
|
int evenSum = count(getEvenCounts());
|
|
int mismatch = oddSum + evenSum - numModules;
|
|
bool oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);
|
|
bool evenParityBad = (evenSum & 0x01) == 1;
|
|
|
|
bool incrementOdd = false;
|
|
bool decrementOdd = false;
|
|
bool incrementEven = false;
|
|
bool decrementEven = false;
|
|
|
|
if (outsideChar)
|
|
{
|
|
if (oddSum > 12)
|
|
{
|
|
decrementOdd = true;
|
|
}
|
|
else if (oddSum < 4)
|
|
{
|
|
incrementOdd = true;
|
|
}
|
|
if (evenSum > 12)
|
|
{
|
|
decrementEven = true;
|
|
}
|
|
else if (evenSum < 4)
|
|
{
|
|
incrementEven = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (oddSum > 11)
|
|
{
|
|
decrementOdd = true;
|
|
}
|
|
else if (oddSum < 5)
|
|
{
|
|
incrementOdd = true;
|
|
}
|
|
if (evenSum > 10)
|
|
{
|
|
decrementEven = true;
|
|
}
|
|
else if (evenSum < 4)
|
|
{
|
|
incrementEven = true;
|
|
}
|
|
}
|
|
|
|
/*if (mismatch == 2) {
|
|
if (!(oddParityBad && evenParityBad)) {
|
|
throw ReaderException.Instance;
|
|
}
|
|
decrementOdd = true;
|
|
decrementEven = true;
|
|
} else if (mismatch == -2) {
|
|
if (!(oddParityBad && evenParityBad)) {
|
|
throw ReaderException.Instance;
|
|
}
|
|
incrementOdd = true;
|
|
incrementEven = true;
|
|
} else */
|
|
if (mismatch == 1)
|
|
{
|
|
if (oddParityBad)
|
|
{
|
|
if (evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
decrementOdd = true;
|
|
}
|
|
else
|
|
{
|
|
if (!evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
decrementEven = true;
|
|
}
|
|
}
|
|
else if (mismatch == -1)
|
|
{
|
|
if (oddParityBad)
|
|
{
|
|
if (evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
incrementOdd = true;
|
|
}
|
|
else
|
|
{
|
|
if (!evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
incrementEven = true;
|
|
}
|
|
}
|
|
else if (mismatch == 0)
|
|
{
|
|
if (oddParityBad)
|
|
{
|
|
if (!evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
// Both bad
|
|
if (oddSum < evenSum)
|
|
{
|
|
incrementOdd = true;
|
|
decrementEven = true;
|
|
}
|
|
else
|
|
{
|
|
decrementOdd = true;
|
|
incrementEven = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (evenParityBad)
|
|
{
|
|
return false;
|
|
}
|
|
// Nothing to do!
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (incrementOdd)
|
|
{
|
|
if (decrementOdd)
|
|
{
|
|
return false;
|
|
}
|
|
increment(getOddCounts(), getOddRoundingErrors());
|
|
}
|
|
if (decrementOdd)
|
|
{
|
|
decrement(getOddCounts(), getOddRoundingErrors());
|
|
}
|
|
if (incrementEven)
|
|
{
|
|
if (decrementEven)
|
|
{
|
|
return false;
|
|
}
|
|
increment(getEvenCounts(), getOddRoundingErrors());
|
|
}
|
|
if (decrementEven)
|
|
{
|
|
decrement(getEvenCounts(), getEvenRoundingErrors());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} |