/* * 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; } } }