/*
* 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 ZXing.Common;
using ZXing.Multi.QrCode.Internal;
using ZXing.QrCode;
using ZXing.QrCode.Internal;
namespace ZXing.Multi.QrCode
{
///
/// This implementation can detect and decode multiple QR Codes in an image.
///
public sealed class QRCodeMultiReader : QRCodeReader, MultipleBarcodeReader
{
private static readonly ResultPoint[] NO_POINTS = new ResultPoint[0];
///
/// Decodes the multiple.
///
/// The image.
///
public Result[] decodeMultiple(BinaryBitmap image)
{
return decodeMultiple(image, null);
}
///
/// Decodes the multiple.
///
/// The image.
/// The hints.
///
public Result[] decodeMultiple(BinaryBitmap image, IDictionary hints)
{
var results = new List();
var detectorResults = new MultiDetector(image.BlackMatrix).detectMulti(hints);
foreach (DetectorResult detectorResult in detectorResults)
{
var decoderResult = getDecoder().decode(detectorResult.Bits, hints);
if (decoderResult == null)
continue;
var points = detectorResult.Points;
// If the code was mirrored: swap the bottom-left and the top-right points.
var data = decoderResult.Other as QRCodeDecoderMetaData;
if (data != null)
{
data.applyMirroredCorrection(points);
}
var result = new Result(decoderResult.Text, decoderResult.RawBytes, points, BarcodeFormat.QR_CODE);
var byteSegments = decoderResult.ByteSegments;
if (byteSegments != null)
{
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
}
var ecLevel = decoderResult.ECLevel;
if (ecLevel != null)
{
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
}
if (decoderResult.StructuredAppend)
{
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE, decoderResult.StructuredAppendSequenceNumber);
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY, decoderResult.StructuredAppendParity);
}
results.Add(result);
}
if (results.Count == 0)
{
return null;
}
results = ProcessStructuredAppend(results);
return results.ToArray();
}
private List ProcessStructuredAppend(List results)
{
bool hasSA = false;
// first, check, if there is at least on SA result in the list
foreach (var result in results)
{
if (result.ResultMetadata.ContainsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE))
{
hasSA = true;
break;
}
}
if (!hasSA)
{
return results;
}
// it is, second, split the lists and built a new result list
var newResults = new List();
var saResults = new List();
foreach (var result in results)
{
newResults.Add(result);
if (result.ResultMetadata.ContainsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE))
{
saResults.Add(result);
}
}
// sort and concatenate the SA list items
saResults.Sort(SaSequenceSort);
var concatedText = String.Empty;
var rawBytesLen = 0;
int byteSegmentLength = 0;
foreach (var saResult in saResults)
{
concatedText += saResult.Text;
rawBytesLen += saResult.RawBytes.Length;
if (saResult.ResultMetadata.ContainsKey(ResultMetadataType.BYTE_SEGMENTS))
{
foreach (var segment in (IEnumerable) saResult.ResultMetadata[ResultMetadataType.BYTE_SEGMENTS])
{
byteSegmentLength += segment.Length;
}
}
}
var newRawBytes = new byte[rawBytesLen];
byte[] newByteSegment = new byte[byteSegmentLength];
int newRawBytesIndex = 0;
int byteSegmentIndex = 0;
foreach (var saResult in saResults)
{
Array.Copy(saResult.RawBytes, 0, newRawBytes, newRawBytesIndex, saResult.RawBytes.Length);
newRawBytesIndex += saResult.RawBytes.Length;
if (saResult.ResultMetadata.ContainsKey(ResultMetadataType.BYTE_SEGMENTS))
{
foreach (var segment in (IEnumerable) saResult.ResultMetadata[ResultMetadataType.BYTE_SEGMENTS])
{
Array.Copy(segment, 0, newByteSegment, byteSegmentIndex, segment.Length);
byteSegmentIndex += segment.Length;
}
}
}
Result newResult = new Result(concatedText, newRawBytes, NO_POINTS, BarcodeFormat.QR_CODE);
if (byteSegmentLength > 0)
{
var byteSegmentList = new List();
byteSegmentList.Add(newByteSegment);
newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegmentList);
}
newResults.Add(newResult);
return newResults;
}
private int SaSequenceSort(Result a, Result b)
{
var aNumber = (int)(a.ResultMetadata[ResultMetadataType.STRUCTURED_APPEND_SEQUENCE]);
var bNumber = (int)(b.ResultMetadata[ResultMetadataType.STRUCTURED_APPEND_SEQUENCE]);
return aNumber - bNumber;
}
}
}