mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-28 20:23:25 +08:00
项目结构调整
This commit is contained in:
263
Others/zxing.core/zxing.core/qrcode/QRCodeReader.cs
Normal file
263
Others/zxing.core/zxing.core/qrcode/QRCodeReader.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2007 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.QrCode.Internal;
|
||||
|
||||
namespace ZXing.QrCode
|
||||
{
|
||||
/// <summary>
|
||||
/// This implementation can detect and decode QR Codes in an image.
|
||||
/// <author>Sean Owen</author>
|
||||
/// </summary>
|
||||
public class QRCodeReader : Reader
|
||||
{
|
||||
private static readonly ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
|
||||
private readonly Decoder decoder = new Decoder();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decoder.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected Decoder getDecoder()
|
||||
{
|
||||
return decoder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates and decodes a QR code in an image.
|
||||
///
|
||||
/// <returns>a String representing the content encoded by the QR code</returns>
|
||||
/// </summary>
|
||||
public Result decode(BinaryBitmap image)
|
||||
{
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates and decodes a barcode in some format within an image. This method also accepts
|
||||
/// hints, each possibly associated to some data, which may help the implementation decode.
|
||||
/// </summary>
|
||||
/// <param name="image">image of barcode to decode</param>
|
||||
/// <param name="hints">passed as a <see cref="IDictionary{TKey, TValue}"/> from <see cref="DecodeHintType"/>
|
||||
/// to arbitrary data. The
|
||||
/// meaning of the data depends upon the hint type. The implementation may or may not do
|
||||
/// anything with these hints.</param>
|
||||
/// <returns>
|
||||
/// String which the barcode encodes
|
||||
/// </returns>
|
||||
public Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
DecoderResult decoderResult;
|
||||
ResultPoint[] points;
|
||||
if (image == null || image.BlackMatrix == null)
|
||||
{
|
||||
// something is wrong with the image
|
||||
return null;
|
||||
}
|
||||
if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE))
|
||||
{
|
||||
var bits = extractPureBits(image.BlackMatrix);
|
||||
if (bits == null)
|
||||
return null;
|
||||
decoderResult = decoder.decode(bits, hints);
|
||||
points = NO_POINTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
var detectorResult = new Detector(image.BlackMatrix).detect(hints);
|
||||
if (detectorResult == null)
|
||||
return null;
|
||||
decoderResult = decoder.decode(detectorResult.Bits, hints);
|
||||
points = detectorResult.Points;
|
||||
}
|
||||
if (decoderResult == null)
|
||||
return null;
|
||||
|
||||
// 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);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets any internal state the implementation has after a decode, to prepare it
|
||||
/// for reuse.
|
||||
/// </summary>
|
||||
public void reset()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
/// which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
/// around it. This is a specialized method that works exceptionally fast in this special
|
||||
/// case.
|
||||
///
|
||||
/// <seealso cref="ZXing.Datamatrix.DataMatrixReader.extractPureBits(BitMatrix)" />
|
||||
/// </summary>
|
||||
private static BitMatrix extractPureBits(BitMatrix image)
|
||||
{
|
||||
int[] leftTopBlack = image.getTopLeftOnBit();
|
||||
int[] rightBottomBlack = image.getBottomRightOnBit();
|
||||
if (leftTopBlack == null || rightBottomBlack == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
float moduleSize;
|
||||
if (!QRCodeReader.moduleSize(leftTopBlack, image, out moduleSize))
|
||||
return null;
|
||||
|
||||
int top = leftTopBlack[1];
|
||||
int bottom = rightBottomBlack[1];
|
||||
int left = leftTopBlack[0];
|
||||
int right = rightBottomBlack[0];
|
||||
|
||||
// Sanity check!
|
||||
if (left >= right || top >= bottom)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bottom - top != right - left)
|
||||
{
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
right = left + (bottom - top);
|
||||
if (right >= image.Width)
|
||||
{
|
||||
// Abort if that would not make sense -- off image
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int matrixWidth = (int)Math.Round((right - left + 1) / moduleSize);
|
||||
int matrixHeight = (int)Math.Round((bottom - top + 1) / moduleSize);
|
||||
if (matrixWidth <= 0 || matrixHeight <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (matrixHeight != matrixWidth)
|
||||
{
|
||||
// Only possibly decode square regions
|
||||
return null;
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
int nudge = (int)(moduleSize / 2.0f);
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
int nudgedTooFarRight = left + (int)((matrixWidth - 1) * moduleSize) - right;
|
||||
if (nudgedTooFarRight > 0)
|
||||
{
|
||||
if (nudgedTooFarRight > nudge)
|
||||
{
|
||||
// Neither way fits; abort
|
||||
return null;
|
||||
}
|
||||
left -= nudgedTooFarRight;
|
||||
}
|
||||
// See logic above
|
||||
int nudgedTooFarDown = top + (int)((matrixHeight - 1) * moduleSize) - bottom;
|
||||
if (nudgedTooFarDown > 0)
|
||||
{
|
||||
if (nudgedTooFarDown > nudge)
|
||||
{
|
||||
// Neither way fits; abort
|
||||
return null;
|
||||
}
|
||||
top -= nudgedTooFarDown;
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
|
||||
for (int y = 0; y < matrixHeight; y++)
|
||||
{
|
||||
int iOffset = top + (int)(y * moduleSize);
|
||||
for (int x = 0; x < matrixWidth; x++)
|
||||
{
|
||||
if (image[left + (int)(x * moduleSize), iOffset])
|
||||
{
|
||||
bits[x, y] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static bool moduleSize(int[] leftTopBlack, BitMatrix image, out float msize)
|
||||
{
|
||||
int height = image.Height;
|
||||
int width = image.Width;
|
||||
int x = leftTopBlack[0];
|
||||
int y = leftTopBlack[1];
|
||||
bool inBlack = true;
|
||||
int transitions = 0;
|
||||
while (x < width && y < height)
|
||||
{
|
||||
if (inBlack != image[x, y])
|
||||
{
|
||||
if (++transitions == 5)
|
||||
{
|
||||
break;
|
||||
}
|
||||
inBlack = !inBlack;
|
||||
}
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
if (x == width || y == height)
|
||||
{
|
||||
msize = 0.0f;
|
||||
return false;
|
||||
}
|
||||
msize = (x - leftTopBlack[0]) / 7.0f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Others/zxing.core/zxing.core/qrcode/QRCodeWriter.cs
Normal file
168
Others/zxing.core/zxing.core/qrcode/QRCodeWriter.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2008 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.QrCode.Internal;
|
||||
|
||||
namespace ZXing.QrCode
|
||||
{
|
||||
/// <summary>
|
||||
/// This object renders a QR Code as a BitMatrix 2D array of greyscale values.
|
||||
///
|
||||
/// <author>dswitkin@google.com (Daniel Switkin)</author>
|
||||
/// </summary>
|
||||
public sealed class QRCodeWriter : Writer
|
||||
{
|
||||
private const int QUIET_ZONE_SIZE = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Encode a barcode using the default settings.
|
||||
/// </summary>
|
||||
/// <param name="contents">The contents to encode in the barcode</param>
|
||||
/// <param name="format">The barcode format to generate</param>
|
||||
/// <param name="width">The preferred width in pixels</param>
|
||||
/// <param name="height">The preferred height in pixels</param>
|
||||
/// <returns>
|
||||
/// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
|
||||
/// </returns>
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
|
||||
{
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="contents">The contents to encode in the barcode</param>
|
||||
/// <param name="format">The barcode format to generate</param>
|
||||
/// <param name="width">The preferred width in pixels</param>
|
||||
/// <param name="height">The preferred height in pixels</param>
|
||||
/// <param name="hints">Additional parameters to supply to the encoder</param>
|
||||
/// <returns>
|
||||
/// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
|
||||
/// </returns>
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
IDictionary<EncodeHintType, object> hints)
|
||||
{
|
||||
if (String.IsNullOrEmpty(contents))
|
||||
{
|
||||
throw new ArgumentException("Found empty contents");
|
||||
}
|
||||
|
||||
if (format != BarcodeFormat.QR_CODE)
|
||||
{
|
||||
throw new ArgumentException("Can only encode QR_CODE, but got " + format);
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0)
|
||||
{
|
||||
throw new ArgumentException("Requested dimensions are too small: " + width + 'x' + height);
|
||||
}
|
||||
|
||||
var errorCorrectionLevel = ErrorCorrectionLevel.L;
|
||||
int quietZone = QUIET_ZONE_SIZE;
|
||||
if (hints != null)
|
||||
{
|
||||
if (hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
|
||||
{
|
||||
var requestedECLevel = hints[EncodeHintType.ERROR_CORRECTION];
|
||||
if (requestedECLevel != null)
|
||||
{
|
||||
errorCorrectionLevel = requestedECLevel as ErrorCorrectionLevel;
|
||||
if (errorCorrectionLevel == null)
|
||||
{
|
||||
switch (requestedECLevel.ToString().ToUpper())
|
||||
{
|
||||
case "L":
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.L;
|
||||
break;
|
||||
case "M":
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.M;
|
||||
break;
|
||||
case "Q":
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.Q;
|
||||
break;
|
||||
case "H":
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.H;
|
||||
break;
|
||||
default:
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.L;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hints.ContainsKey(EncodeHintType.MARGIN))
|
||||
{
|
||||
var quietZoneInt = hints[EncodeHintType.MARGIN];
|
||||
if (quietZoneInt != null)
|
||||
{
|
||||
quietZone = Convert.ToInt32(quietZoneInt.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var code = Encoder.encode(contents, errorCorrectionLevel, hints);
|
||||
return renderResult(code, width, height, quietZone);
|
||||
}
|
||||
|
||||
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
|
||||
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
|
||||
private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone)
|
||||
{
|
||||
var input = code.Matrix;
|
||||
if (input == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
int inputWidth = input.Width;
|
||||
int inputHeight = input.Height;
|
||||
int qrWidth = inputWidth + (quietZone << 1);
|
||||
int qrHeight = inputHeight + (quietZone << 1);
|
||||
int outputWidth = Math.Max(width, qrWidth);
|
||||
int outputHeight = Math.Max(height, qrHeight);
|
||||
|
||||
int multiple = Math.Min(outputWidth / qrWidth, outputHeight / qrHeight);
|
||||
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
|
||||
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
|
||||
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
|
||||
// handle all the padding from 100x100 (the actual QR) up to 200x160.
|
||||
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
|
||||
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
|
||||
|
||||
var output = new BitMatrix(outputWidth, outputHeight);
|
||||
|
||||
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple)
|
||||
{
|
||||
// Write the contents of this row of the barcode
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple)
|
||||
{
|
||||
if (input[inputX, inputY] == 1)
|
||||
{
|
||||
output.setRegion(outputX, outputY, multiple, multiple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
279
Others/zxing.core/zxing.core/qrcode/decoder/BitMatrixParser.cs
Normal file
279
Others/zxing.core/zxing.core/qrcode/decoder/BitMatrixParser.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2007 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 ZXing.Common;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <author>Sean Owen</author>
|
||||
sealed class BitMatrixParser
|
||||
{
|
||||
private readonly BitMatrix bitMatrix;
|
||||
private Version parsedVersion;
|
||||
private FormatInformation parsedFormatInfo;
|
||||
private bool mirrored;
|
||||
|
||||
/// <param name="bitMatrix">{@link BitMatrix} to parse</param>
|
||||
/// <throws>ReaderException if dimension is not >= 21 and 1 mod 4</throws>
|
||||
internal static BitMatrixParser createBitMatrixParser(BitMatrix bitMatrix)
|
||||
{
|
||||
int dimension = bitMatrix.Height;
|
||||
if (dimension < 21 || (dimension & 0x03) != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new BitMatrixParser(bitMatrix);
|
||||
}
|
||||
|
||||
private BitMatrixParser(BitMatrix bitMatrix)
|
||||
{
|
||||
// Should only be called from createBitMatrixParser with the important checks before
|
||||
this.bitMatrix = bitMatrix;
|
||||
}
|
||||
|
||||
/// <summary> <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns> {@link FormatInformation} encapsulating the QR Code's format info
|
||||
/// </returns>
|
||||
/// <throws> ReaderException if both format information locations cannot be parsed as </throws>
|
||||
/// <summary> the valid encoding of format information
|
||||
/// </summary>
|
||||
internal FormatInformation readFormatInformation()
|
||||
{
|
||||
if (parsedFormatInfo != null)
|
||||
{
|
||||
return parsedFormatInfo;
|
||||
}
|
||||
|
||||
// Read top-left format info bits
|
||||
int formatInfoBits1 = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
|
||||
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
|
||||
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for (int j = 5; j >= 0; j--)
|
||||
{
|
||||
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
|
||||
}
|
||||
// Read the top-right/bottom-left pattern too
|
||||
int dimension = bitMatrix.Height;
|
||||
int formatInfoBits2 = 0;
|
||||
int jMin = dimension - 7;
|
||||
for (int j = dimension - 1; j >= jMin; j--)
|
||||
{
|
||||
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
|
||||
}
|
||||
for (int i = dimension - 8; i < dimension; i++)
|
||||
{
|
||||
formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
|
||||
}
|
||||
|
||||
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
|
||||
if (parsedFormatInfo != null)
|
||||
{
|
||||
return parsedFormatInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns> {@link Version} encapsulating the QR Code's version
|
||||
/// </returns>
|
||||
/// <throws> ReaderException if both version information locations cannot be parsed as </throws>
|
||||
/// <summary> the valid encoding of version information
|
||||
/// </summary>
|
||||
internal Version readVersion()
|
||||
{
|
||||
if (parsedVersion != null)
|
||||
{
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
int dimension = bitMatrix.Height;
|
||||
|
||||
int provisionalVersion = (dimension - 17) >> 2;
|
||||
if (provisionalVersion <= 6)
|
||||
{
|
||||
return Version.getVersionForNumber(provisionalVersion);
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
int versionBits = 0;
|
||||
int ijMin = dimension - 11;
|
||||
for (int j = 5; j >= 0; j--)
|
||||
{
|
||||
for (int i = dimension - 9; i >= ijMin; i--)
|
||||
{
|
||||
versionBits = copyBit(i, j, versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
parsedVersion = Version.decodeVersionInformation(versionBits);
|
||||
if (parsedVersion != null && parsedVersion.DimensionForVersion == dimension)
|
||||
{
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
versionBits = 0;
|
||||
for (int i = 5; i >= 0; i--)
|
||||
{
|
||||
for (int j = dimension - 9; j >= ijMin; j--)
|
||||
{
|
||||
versionBits = copyBit(i, j, versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
parsedVersion = Version.decodeVersionInformation(versionBits);
|
||||
if (parsedVersion != null && parsedVersion.DimensionForVersion == dimension)
|
||||
{
|
||||
return parsedVersion;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int copyBit(int i, int j, int versionBits)
|
||||
{
|
||||
bool bit = mirrored ? bitMatrix[j, i] : bitMatrix[i, j];
|
||||
return bit ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||
}
|
||||
|
||||
/// <summary> <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
/// correct order in order to reconstruct the codewords bytes contained within the
|
||||
/// QR Code.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns> bytes encoded within the QR Code
|
||||
/// </returns>
|
||||
/// <throws> ReaderException if the exact number of bytes expected is not read </throws>
|
||||
internal byte[] readCodewords()
|
||||
{
|
||||
FormatInformation formatInfo = readFormatInformation();
|
||||
if (formatInfo == null)
|
||||
return null;
|
||||
Version version = readVersion();
|
||||
if (version == null)
|
||||
return null;
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
int dimension = bitMatrix.Height;
|
||||
DataMask.unmaskBitMatrix(formatInfo.DataMask, bitMatrix, dimension);
|
||||
|
||||
BitMatrix functionPattern = version.buildFunctionPattern();
|
||||
|
||||
bool readingUp = true;
|
||||
byte[] result = new byte[version.TotalCodewords];
|
||||
int resultOffset = 0;
|
||||
int currentByte = 0;
|
||||
int bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for (int j = dimension - 1; j > 0; j -= 2)
|
||||
{
|
||||
if (j == 6)
|
||||
{
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for (int count = 0; count < dimension; count++)
|
||||
{
|
||||
int i = readingUp ? dimension - 1 - count : count;
|
||||
for (int col = 0; col < 2; col++)
|
||||
{
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!functionPattern[j - col, i])
|
||||
{
|
||||
// Read a bit
|
||||
bitsRead++;
|
||||
currentByte <<= 1;
|
||||
if (bitMatrix[j - col, i])
|
||||
{
|
||||
currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if (bitsRead == 8)
|
||||
{
|
||||
result[resultOffset++] = (byte)currentByte;
|
||||
bitsRead = 0;
|
||||
currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if (resultOffset != version.TotalCodewords)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
internal void remask()
|
||||
{
|
||||
if (parsedFormatInfo == null)
|
||||
{
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
int dimension = bitMatrix.Height;
|
||||
DataMask.unmaskBitMatrix(parsedFormatInfo.DataMask, bitMatrix, dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param mirror Whether to read version and format information mirrored.
|
||||
*/
|
||||
internal void setMirror(bool mirror)
|
||||
{
|
||||
parsedVersion = null;
|
||||
parsedFormatInfo = null;
|
||||
mirrored = mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
internal void mirror()
|
||||
{
|
||||
for (int x = 0; x < bitMatrix.Width; x++)
|
||||
{
|
||||
for (int y = x + 1; y < bitMatrix.Height; y++)
|
||||
{
|
||||
if (bitMatrix[x, y] != bitMatrix[y, x])
|
||||
{
|
||||
bitMatrix.flip(y, x);
|
||||
bitMatrix.flip(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
Others/zxing.core/zxing.core/qrcode/decoder/DataBlock.cs
Normal file
146
Others/zxing.core/zxing.core/qrcode/decoder/DataBlock.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2007 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.
|
||||
*/
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary> <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
|
||||
/// multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
/// is represented by an instance of this class.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author> Sean Owen
|
||||
/// </author>
|
||||
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
|
||||
/// </author>
|
||||
internal sealed class DataBlock
|
||||
{
|
||||
private readonly int numDataCodewords;
|
||||
private readonly byte[] codewords;
|
||||
|
||||
private DataBlock(int numDataCodewords, byte[] codewords)
|
||||
{
|
||||
this.numDataCodewords = numDataCodewords;
|
||||
this.codewords = codewords;
|
||||
}
|
||||
|
||||
/// <summary> <p>When QR Codes use multiple data blocks, they are actually interleaved.
|
||||
/// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
/// method will separate the data into original blocks.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="rawCodewords">bytes as read directly from the QR Code
|
||||
/// </param>
|
||||
/// <param name="version">version of the QR Code
|
||||
/// </param>
|
||||
/// <param name="ecLevel">error-correction level of the QR Code
|
||||
/// </param>
|
||||
/// <returns> {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
|
||||
/// QR Code
|
||||
/// </returns>
|
||||
internal static DataBlock[] getDataBlocks(byte[] rawCodewords, Version version, ErrorCorrectionLevel ecLevel)
|
||||
{
|
||||
|
||||
if (rawCodewords.Length != version.TotalCodewords)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
|
||||
// First count the total number of data blocks
|
||||
int totalBlocks = 0;
|
||||
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||
foreach (var ecBlock in ecBlockArray)
|
||||
{
|
||||
totalBlocks += ecBlock.Count;
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
DataBlock[] result = new DataBlock[totalBlocks];
|
||||
int numResultBlocks = 0;
|
||||
foreach (var ecBlock in ecBlockArray)
|
||||
{
|
||||
for (int i = 0; i < ecBlock.Count; i++)
|
||||
{
|
||||
int numDataCodewords = ecBlock.DataCodewords;
|
||||
int numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords;
|
||||
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 more byte. Figure out where these start.
|
||||
int shorterBlocksTotalCodewords = result[0].codewords.Length;
|
||||
int longerBlocksStartAt = result.Length - 1;
|
||||
while (longerBlocksStartAt >= 0)
|
||||
{
|
||||
int numCodewords = result[longerBlocksStartAt].codewords.Length;
|
||||
if (numCodewords == shorterBlocksTotalCodewords)
|
||||
{
|
||||
break;
|
||||
}
|
||||
longerBlocksStartAt--;
|
||||
}
|
||||
longerBlocksStartAt++;
|
||||
|
||||
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock;
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
int rawCodewordsOffset = 0;
|
||||
for (int i = 0; i < shorterBlocksNumDataCodewords; i++)
|
||||
{
|
||||
for (int j = 0; j < numResultBlocks; j++)
|
||||
{
|
||||
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for (int j = longerBlocksStartAt; j < numResultBlocks; j++)
|
||||
{
|
||||
result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
int max = result[0].codewords.Length;
|
||||
for (int i = shorterBlocksNumDataCodewords; i < max; i++)
|
||||
{
|
||||
for (int j = 0; j < numResultBlocks; j++)
|
||||
{
|
||||
int iOffset = j < longerBlocksStartAt ? i : i + 1;
|
||||
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal int NumDataCodewords
|
||||
{
|
||||
get
|
||||
{
|
||||
return numDataCodewords;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] Codewords
|
||||
{
|
||||
get
|
||||
{
|
||||
return codewords;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Others/zxing.core/zxing.core/qrcode/decoder/DataMask.cs
Normal file
73
Others/zxing.core/zxing.core/qrcode/decoder/DataMask.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2007 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 ZXing.Common;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary> <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
/// of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
|
||||
/// including areas used for finder patterns, timing patterns, etc. These areas should be unused
|
||||
/// after the point they are unmasked anyway.</p>
|
||||
///
|
||||
/// <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
/// and j is row position. In fact, as the text says, i is row position and j is column position.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
internal static class DataMask
|
||||
{
|
||||
/// <summary> See ISO 18004:2006 6.8.1</summary>
|
||||
private static readonly Func<int, int, bool>[] DATA_MASKS = new Func<int, int, bool>[]
|
||||
{
|
||||
// 000: mask bits for which (x + y) mod 2 == 0
|
||||
new Func<int, int, bool>((i, j) => ((i + j) & 0x01) == 0),
|
||||
// 001: mask bits for which x mod 2 == 0
|
||||
new Func<int, int, bool>((i, j) => (i & 0x01) == 0),
|
||||
// 010: mask bits for which y mod 3 == 0
|
||||
new Func<int, int, bool>((i, j) => j % 3 == 0),
|
||||
// 011: mask bits for which (x + y) mod 3 == 0
|
||||
new Func<int, int, bool>((i, j) => (i + j) % 3 == 0),
|
||||
// 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
||||
new Func<int, int, bool>((i, j) => ((((int)((uint)i >> 1)) + (j / 3)) & 0x01) == 0),
|
||||
// 101: mask bits for which xy mod 2 + xy mod 3 == 0, equivalently, such that xy mod 6 == 0
|
||||
new Func<int, int, bool>((i, j) => (i * j) % 6 == 0),
|
||||
// 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0, equivalently, such that xy mod 6 < 3
|
||||
new Func<int, int, bool>((i, j) => ((i * j) % 6) < 3),
|
||||
// 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0, equivalently, such that (x + y + xy mod 3) mod 2 == 0
|
||||
new Func<int, int, bool>((i, j) => ((i + j + ((i * j) % 3)) & 0x01) == 0),
|
||||
};
|
||||
|
||||
/// <summary> <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
/// make its bits ready to read.</p>
|
||||
/// </summary>
|
||||
/// <param name="reference"></param>
|
||||
/// <param name="bits">representation of QR Code bits</param>
|
||||
/// <param name="dimension">dimension of QR Code, represented by bits, being unmasked</param>
|
||||
internal static void unmaskBitMatrix(int reference, BitMatrix bits, int dimension)
|
||||
{
|
||||
if (reference < 0 || reference > 7)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
var isMasked = DATA_MASKS[reference];
|
||||
|
||||
bits.flipWhen(isMasked);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Copyright 2007 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.QrCode.Internal
|
||||
{
|
||||
/// <summary> <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
/// in one QR Code. This class decodes the bits back into text.</p>
|
||||
///
|
||||
/// <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
/// <author>Sean Owen</author>
|
||||
/// </summary>
|
||||
internal static class DecodedBitStreamParser
|
||||
{
|
||||
/// <summary>
|
||||
/// See ISO 18004:2006, 6.4.4 Table 5
|
||||
/// </summary>
|
||||
private static readonly char[] ALPHANUMERIC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".ToCharArray();
|
||||
private const int GB2312_SUBSET = 1;
|
||||
|
||||
internal static DecoderResult decode(byte[] bytes,
|
||||
Version version,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
var bits = new BitSource(bytes);
|
||||
var result = new StringBuilder(50);
|
||||
var byteSegments = new List<byte[]>(1);
|
||||
var symbolSequence = -1;
|
||||
var parityData = -1;
|
||||
|
||||
try
|
||||
{
|
||||
CharacterSetECI currentCharacterSetECI = null;
|
||||
bool fc1InEffect = false;
|
||||
Mode mode;
|
||||
do
|
||||
{
|
||||
// While still another segment to read...
|
||||
if (bits.available() < 4)
|
||||
{
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
mode = Mode.TERMINATOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
switch (mode.Name)
|
||||
{
|
||||
case Mode.Names.TERMINATOR:
|
||||
break;
|
||||
case Mode.Names.FNC1_FIRST_POSITION:
|
||||
case Mode.Names.FNC1_SECOND_POSITION:
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
fc1InEffect = true;
|
||||
break;
|
||||
case Mode.Names.STRUCTURED_APPEND:
|
||||
if (bits.available() < 16)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// not really supported; but sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
symbolSequence = bits.readBits(8);
|
||||
parityData = bits.readBits(8);
|
||||
break;
|
||||
case Mode.Names.ECI:
|
||||
// Count doesn't apply to ECI
|
||||
int value = parseECIValue(bits);
|
||||
currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
|
||||
if (currentCharacterSetECI == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
case Mode.Names.HANZI:
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
//chinese mode contains a sub set indicator right after mode indicator
|
||||
int subset = bits.readBits(4);
|
||||
int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
|
||||
if (subset == GB2312_SUBSET)
|
||||
{
|
||||
if (!decodeHanziSegment(bits, result, countHanzi))
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
int count = bits.readBits(mode.getCharacterCountBits(version));
|
||||
switch (mode.Name)
|
||||
{
|
||||
case Mode.Names.NUMERIC:
|
||||
if (!decodeNumericSegment(bits, result, count))
|
||||
return null;
|
||||
break;
|
||||
case Mode.Names.ALPHANUMERIC:
|
||||
if (!decodeAlphanumericSegment(bits, result, count, fc1InEffect))
|
||||
return null;
|
||||
break;
|
||||
case Mode.Names.BYTE:
|
||||
if (!decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints))
|
||||
return null;
|
||||
break;
|
||||
case Mode.Names.KANJI:
|
||||
if (!decodeKanjiSegment(bits, result, count))
|
||||
return null;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (mode != Mode.TERMINATOR);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// from readBits() calls
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DecoderResult(bytes,
|
||||
result.ToString(),
|
||||
byteSegments.Count == 0 ? null : byteSegments,
|
||||
ecLevel == null ? null : ecLevel.ToString(),
|
||||
symbolSequence, parityData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See specification GBT 18284-2000
|
||||
/// </summary>
|
||||
/// <param name="bits">The bits.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <returns></returns>
|
||||
private static bool decodeHanziSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (count * 13 > bits.available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
byte[] buffer = new byte[2 * count];
|
||||
int offset = 0;
|
||||
while (count > 0)
|
||||
{
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
int twoBytes = bits.readBits(13);
|
||||
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||
if (assembledTwoBytes < 0x00A00)
|
||||
{
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
assembledTwoBytes += 0x0A1A1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
buffer[offset] = (byte)((assembledTwoBytes >> 8) & 0xFF);
|
||||
buffer[offset + 1] = (byte)(assembledTwoBytes & 0xFF);
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result.Append(Encoding.GetEncoding(StringUtils.GB2312).GetString(buffer, 0, buffer.Length));
|
||||
}
|
||||
#if (WINDOWS_PHONE70 || WINDOWS_PHONE71 || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || MONOANDROID || MONOTOUCH)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Silverlight only supports a limited number of character sets, trying fallback to UTF-8
|
||||
result.Append(Encoding.GetEncoding("UTF-8").GetString(buffer, 0, buffer.Length));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool decodeKanjiSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (count * 13 > bits.available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
byte[] buffer = new byte[2 * count];
|
||||
int offset = 0;
|
||||
while (count > 0)
|
||||
{
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
int twoBytes = bits.readBits(13);
|
||||
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||
if (assembledTwoBytes < 0x01F00)
|
||||
{
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
assembledTwoBytes += 0x08140;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
buffer[offset] = (byte)(assembledTwoBytes >> 8);
|
||||
buffer[offset + 1] = (byte)assembledTwoBytes;
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
try
|
||||
{
|
||||
result.Append(Encoding.GetEncoding(StringUtils.SHIFT_JIS).GetString(buffer, 0, buffer.Length));
|
||||
}
|
||||
#if (WINDOWS_PHONE70 || WINDOWS_PHONE71 || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || MONOANDROID || MONOTOUCH)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Silverlight only supports a limited number of character sets, trying fallback to UTF-8
|
||||
result.Append(Encoding.GetEncoding("UTF-8").GetString(buffer, 0, buffer.Length));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool decodeByteSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count,
|
||||
CharacterSetECI currentCharacterSetECI,
|
||||
IList<byte[]> byteSegments,
|
||||
IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (count << 3 > bits.available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] readBytes = new byte[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
readBytes[i] = (byte)bits.readBits(8);
|
||||
}
|
||||
String encoding;
|
||||
if (currentCharacterSetECI == null)
|
||||
{
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
encoding = StringUtils.guessEncoding(readBytes, hints);
|
||||
}
|
||||
else
|
||||
{
|
||||
encoding = currentCharacterSetECI.EncodingName;
|
||||
}
|
||||
try
|
||||
{
|
||||
result.Append(Encoding.GetEncoding(encoding).GetString(readBytes, 0, readBytes.Length));
|
||||
}
|
||||
#if (WINDOWS_PHONE70 || WINDOWS_PHONE71 || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || MONOANDROID || MONOTOUCH)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Silverlight only supports a limited number of character sets, trying fallback to UTF-8
|
||||
result.Append(Encoding.GetEncoding("UTF-8").GetString(readBytes, 0, readBytes.Length));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if WindowsCE
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// WindowsCE doesn't support all encodings. But it is device depended.
|
||||
// So we try here the some different ones
|
||||
if (encoding == "ISO-8859-1")
|
||||
{
|
||||
result.Append(Encoding.GetEncoding(1252).GetString(readBytes, 0, readBytes.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append(Encoding.GetEncoding("UTF-8").GetString(readBytes, 0, readBytes.Length));
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byteSegments.Add(readBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static char toAlphaNumericChar(int value)
|
||||
{
|
||||
if (value >= ALPHANUMERIC_CHARS.Length)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
return ALPHANUMERIC_CHARS[value];
|
||||
}
|
||||
|
||||
private static bool decodeAlphanumericSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count,
|
||||
bool fc1InEffect)
|
||||
{
|
||||
// Read two characters at a time
|
||||
int start = result.Length;
|
||||
while (count > 1)
|
||||
{
|
||||
if (bits.available() < 11)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int nextTwoCharsBits = bits.readBits(11);
|
||||
result.Append(toAlphaNumericChar(nextTwoCharsBits / 45));
|
||||
result.Append(toAlphaNumericChar(nextTwoCharsBits % 45));
|
||||
count -= 2;
|
||||
}
|
||||
if (count == 1)
|
||||
{
|
||||
// special case: one character left
|
||||
if (bits.available() < 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.Append(toAlphaNumericChar(bits.readBits(6)));
|
||||
}
|
||||
|
||||
// See section 6.4.8.1, 6.4.8.2
|
||||
if (fc1InEffect)
|
||||
{
|
||||
// We need to massage the result a bit if in an FNC1 mode:
|
||||
for (int i = start; i < result.Length; i++)
|
||||
{
|
||||
if (result[i] == '%')
|
||||
{
|
||||
if (i < result.Length - 1 && result[i + 1] == '%')
|
||||
{
|
||||
// %% is rendered as %
|
||||
result.Remove(i + 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
result.Remove(i, 1);
|
||||
result.Insert(i, new[] { (char)0x1D });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool decodeNumericSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count)
|
||||
{
|
||||
// Read three digits at a time
|
||||
while (count >= 3)
|
||||
{
|
||||
// Each 10 bits encodes three digits
|
||||
if (bits.available() < 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int threeDigitsBits = bits.readBits(10);
|
||||
if (threeDigitsBits >= 1000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.Append(toAlphaNumericChar(threeDigitsBits / 100));
|
||||
result.Append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
|
||||
result.Append(toAlphaNumericChar(threeDigitsBits % 10));
|
||||
|
||||
count -= 3;
|
||||
}
|
||||
if (count == 2)
|
||||
{
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if (bits.available() < 7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int twoDigitsBits = bits.readBits(7);
|
||||
if (twoDigitsBits >= 100)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.Append(toAlphaNumericChar(twoDigitsBits / 10));
|
||||
result.Append(toAlphaNumericChar(twoDigitsBits % 10));
|
||||
}
|
||||
else if (count == 1)
|
||||
{
|
||||
// One digit left over to read
|
||||
if (bits.available() < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int digitBits = bits.readBits(4);
|
||||
if (digitBits >= 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.Append(toAlphaNumericChar(digitBits));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int parseECIValue(BitSource bits)
|
||||
{
|
||||
int firstByte = bits.readBits(8);
|
||||
if ((firstByte & 0x80) == 0)
|
||||
{
|
||||
// just one byte
|
||||
return firstByte & 0x7F;
|
||||
}
|
||||
if ((firstByte & 0xC0) == 0x80)
|
||||
{
|
||||
// two bytes
|
||||
int secondByte = bits.readBits(8);
|
||||
return ((firstByte & 0x3F) << 8) | secondByte;
|
||||
}
|
||||
if ((firstByte & 0xE0) == 0xC0)
|
||||
{
|
||||
// three bytes
|
||||
int secondThirdBytes = bits.readBits(16);
|
||||
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
|
||||
}
|
||||
throw new ArgumentException("Bad ECI bits starting with byte " + firstByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
Others/zxing.core/zxing.core/qrcode/decoder/Decoder.cs
Normal file
186
Others/zxing.core/zxing.core/qrcode/decoder/Decoder.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2007 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.Collections.Generic;
|
||||
|
||||
using ZXing.Common;
|
||||
using ZXing.Common.ReedSolomon;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
/// the QR Code from an image.</p>
|
||||
/// </summary>
|
||||
/// <author>
|
||||
/// Sean Owen
|
||||
/// </author>
|
||||
public sealed class Decoder
|
||||
{
|
||||
private readonly ReedSolomonDecoder rsDecoder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Decoder"/> class.
|
||||
/// </summary>
|
||||
public Decoder()
|
||||
{
|
||||
rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
/// "true" is taken to mean a black module.</p>
|
||||
/// </summary>
|
||||
/// <param name="image">booleans representing white/black QR Code modules</param>
|
||||
/// <param name="hints">decoding hints that should be used to influence decoding</param>
|
||||
/// <returns>
|
||||
/// text and bytes encoded within the QR Code
|
||||
/// </returns>
|
||||
public DecoderResult decode(bool[][] image, IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
return decode(BitMatrix.parse(image), hints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
/// </summary>
|
||||
/// <param name="bits">booleans representing white/black QR Code modules</param>
|
||||
/// <param name="hints">decoding hints that should be used to influence decoding</param>
|
||||
/// <returns>
|
||||
/// text and bytes encoded within the QR Code
|
||||
/// </returns>
|
||||
public DecoderResult decode(BitMatrix bits, IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
// Construct a parser and read version, error-correction level
|
||||
var parser = BitMatrixParser.createBitMatrixParser(bits);
|
||||
if (parser == null)
|
||||
return null;
|
||||
|
||||
var result = decode(parser, hints);
|
||||
if (result == null)
|
||||
{
|
||||
// Revert the bit matrix
|
||||
parser.remask();
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
parser.setMirror(true);
|
||||
|
||||
// Preemptively read the version.
|
||||
var version = parser.readVersion();
|
||||
if (version == null)
|
||||
return null;
|
||||
|
||||
// Preemptively read the format information.
|
||||
var formatinfo = parser.readFormatInformation();
|
||||
if (formatinfo == null)
|
||||
return null;
|
||||
|
||||
/*
|
||||
* Since we're here, this means we have successfully detected some kind
|
||||
* of version and format information when mirrored. This is a good sign,
|
||||
* that the QR code may be mirrored, and we should try once more with a
|
||||
* mirrored content.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
parser.mirror();
|
||||
|
||||
result = decode(parser, hints);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
result.Other = new QRCodeDecoderMetaData(true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private DecoderResult decode(BitMatrixParser parser, IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
Version version = parser.readVersion();
|
||||
if (version == null)
|
||||
return null;
|
||||
var formatinfo = parser.readFormatInformation();
|
||||
if (formatinfo == null)
|
||||
return null;
|
||||
ErrorCorrectionLevel ecLevel = formatinfo.ErrorCorrectionLevel;
|
||||
|
||||
// Read codewords
|
||||
byte[] codewords = parser.readCodewords();
|
||||
if (codewords == null)
|
||||
return null;
|
||||
// Separate into data blocks
|
||||
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
|
||||
|
||||
// Count total number of data bytes
|
||||
int totalBytes = 0;
|
||||
foreach (var dataBlock in dataBlocks)
|
||||
{
|
||||
totalBytes += dataBlock.NumDataCodewords;
|
||||
}
|
||||
byte[] resultBytes = new byte[totalBytes];
|
||||
int resultOffset = 0;
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
foreach (var dataBlock in dataBlocks)
|
||||
{
|
||||
byte[] codewordBytes = dataBlock.Codewords;
|
||||
int numDataCodewords = dataBlock.NumDataCodewords;
|
||||
if (!correctErrors(codewordBytes, numDataCodewords))
|
||||
return null;
|
||||
for (int i = 0; i < numDataCodewords; i++)
|
||||
{
|
||||
resultBytes[resultOffset++] = codewordBytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
/// correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
/// </summary>
|
||||
/// <param name="codewordBytes">data and error correction codewords</param>
|
||||
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
|
||||
/// <returns></returns>
|
||||
private bool correctErrors(byte[] codewordBytes, int numDataCodewords)
|
||||
{
|
||||
int numCodewords = codewordBytes.Length;
|
||||
// First read into an array of ints
|
||||
int[] codewordsInts = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++)
|
||||
{
|
||||
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||
}
|
||||
int numECCodewords = codewordBytes.Length - numDataCodewords;
|
||||
|
||||
if (!rsDecoder.decode(codewordsInts, numECCodewords))
|
||||
return false;
|
||||
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for (int i = 0; i < numDataCodewords; i++)
|
||||
{
|
||||
codewordBytes[i] = (byte)codewordsInts[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
|
||||
/// defined by the QR code standard.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public sealed class ErrorCorrectionLevel
|
||||
{
|
||||
/// <summary> L = ~7% correction</summary>
|
||||
public static readonly ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L");
|
||||
/// <summary> M = ~15% correction</summary>
|
||||
public static readonly ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M");
|
||||
/// <summary> Q = ~25% correction</summary>
|
||||
public static readonly ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q");
|
||||
/// <summary> H = ~30% correction</summary>
|
||||
public static readonly ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H");
|
||||
|
||||
private static readonly ErrorCorrectionLevel[] FOR_BITS = new[] { M, L, H, Q };
|
||||
|
||||
private readonly int bits;
|
||||
|
||||
private ErrorCorrectionLevel(int ordinal, int bits, String name)
|
||||
{
|
||||
this.ordinal_Renamed_Field = ordinal;
|
||||
this.bits = bits;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits.
|
||||
/// </summary>
|
||||
public int Bits
|
||||
{
|
||||
get
|
||||
{
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public String Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly int ordinal_Renamed_Field;
|
||||
private readonly String name;
|
||||
|
||||
/// <summary>
|
||||
/// Ordinals this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int ordinal()
|
||||
{
|
||||
return ordinal_Renamed_Field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String"/> that represents this instance.
|
||||
/// </returns>
|
||||
public override String ToString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fors the bits.
|
||||
/// </summary>
|
||||
/// <param name="bits">int containing the two bits encoding a QR Code's error correction level</param>
|
||||
/// <returns>
|
||||
/// <see cref="ErrorCorrectionLevel"/> representing the encoded error correction level
|
||||
/// </returns>
|
||||
public static ErrorCorrectionLevel forBits(int bits)
|
||||
{
|
||||
if (bits < 0 || bits >= FOR_BITS.Length)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
return FOR_BITS[bits];
|
||||
}
|
||||
}
|
||||
}
|
||||
197
Others/zxing.core/zxing.core/qrcode/decoder/FormatInformation.cs
Normal file
197
Others/zxing.core/zxing.core/qrcode/decoder/FormatInformation.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
|
||||
/// <summary> <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
/// error correction level.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author> Sean Owen
|
||||
/// </author>
|
||||
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
|
||||
/// </author>
|
||||
/// <seealso cref="DataMask">
|
||||
/// </seealso>
|
||||
/// <seealso cref="ErrorCorrectionLevel">
|
||||
/// </seealso>
|
||||
sealed class FormatInformation
|
||||
{
|
||||
private const int FORMAT_INFO_MASK_QR = 0x5412;
|
||||
|
||||
/// <summary> See ISO 18004:2006, Annex C, Table C.1</summary>
|
||||
private static readonly int[][] FORMAT_INFO_DECODE_LOOKUP = new int[][]
|
||||
{
|
||||
new [] { 0x5412, 0x00 },
|
||||
new [] { 0x5125, 0x01 },
|
||||
new [] { 0x5E7C, 0x02 },
|
||||
new [] { 0x5B4B, 0x03 },
|
||||
new [] { 0x45F9, 0x04 },
|
||||
new [] { 0x40CE, 0x05 },
|
||||
new [] { 0x4F97, 0x06 },
|
||||
new [] { 0x4AA0, 0x07 },
|
||||
new [] { 0x77C4, 0x08 },
|
||||
new [] { 0x72F3, 0x09 },
|
||||
new [] { 0x7DAA, 0x0A },
|
||||
new [] { 0x789D, 0x0B },
|
||||
new [] { 0x662F, 0x0C },
|
||||
new [] { 0x6318, 0x0D },
|
||||
new [] { 0x6C41, 0x0E },
|
||||
new [] { 0x6976, 0x0F },
|
||||
new [] { 0x1689, 0x10 },
|
||||
new [] { 0x13BE, 0x11 },
|
||||
new [] { 0x1CE7, 0x12 },
|
||||
new [] { 0x19D0, 0x13 },
|
||||
new [] { 0x0762, 0x14 },
|
||||
new [] { 0x0255, 0x15 },
|
||||
new [] { 0x0D0C, 0x16 },
|
||||
new [] { 0x083B, 0x17 },
|
||||
new [] { 0x355F, 0x18 },
|
||||
new [] { 0x3068, 0x19 },
|
||||
new [] { 0x3F31, 0x1A },
|
||||
new [] { 0x3A06, 0x1B },
|
||||
new [] { 0x24B4, 0x1C },
|
||||
new [] { 0x2183, 0x1D },
|
||||
new [] { 0x2EDA, 0x1E },
|
||||
new [] { 0x2BED, 0x1F }
|
||||
};
|
||||
|
||||
/// <summary> Offset i holds the number of 1 bits in the binary representation of i</summary>
|
||||
private static readonly int[] BITS_SET_IN_HALF_BYTE = new[]
|
||||
{ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
|
||||
|
||||
private readonly ErrorCorrectionLevel errorCorrectionLevel;
|
||||
private readonly byte dataMask;
|
||||
|
||||
private FormatInformation(int formatInfo)
|
||||
{
|
||||
// Bits 3,4
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
dataMask = (byte)(formatInfo & 0x07);
|
||||
}
|
||||
|
||||
internal static int numBitsDiffering(int a, int b)
|
||||
{
|
||||
a ^= b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return BITS_SET_IN_HALF_BYTE[a & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 4)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 8)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 12)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 16)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 20)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 24)) & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[((int)((uint)a >> 28)) & 0x0F];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the format information.
|
||||
/// </summary>
|
||||
/// <param name="maskedFormatInfo1">format info indicator, with mask still applied</param>
|
||||
/// <param name="maskedFormatInfo2">The masked format info2.</param>
|
||||
/// <returns>
|
||||
/// information about the format it specifies, or <code>null</code>
|
||||
/// if doesn't seem to match any known pattern
|
||||
/// </returns>
|
||||
internal static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2)
|
||||
{
|
||||
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
|
||||
if (formatInfo != null)
|
||||
{
|
||||
return formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
|
||||
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
|
||||
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2)
|
||||
{
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
int bestDifference = Int32.MaxValue;
|
||||
int bestFormatInfo = 0;
|
||||
foreach (var decodeInfo in FORMAT_INFO_DECODE_LOOKUP)
|
||||
{
|
||||
int targetInfo = decodeInfo[0];
|
||||
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2)
|
||||
{
|
||||
// Found an exact match
|
||||
return new FormatInformation(decodeInfo[1]);
|
||||
}
|
||||
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
|
||||
if (bitsDifference < bestDifference)
|
||||
{
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
if (maskedFormatInfo1 != maskedFormatInfo2)
|
||||
{
|
||||
// also try the other option
|
||||
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
|
||||
if (bitsDifference < bestDifference)
|
||||
{
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
|
||||
// differing means we found a match
|
||||
if (bestDifference <= 3)
|
||||
{
|
||||
return new FormatInformation(bestFormatInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal ErrorCorrectionLevel ErrorCorrectionLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorCorrectionLevel;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte DataMask
|
||||
{
|
||||
get
|
||||
{
|
||||
return dataMask;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (errorCorrectionLevel.ordinal() << 3) | dataMask;
|
||||
}
|
||||
|
||||
public override bool Equals(Object o)
|
||||
{
|
||||
if (!(o is FormatInformation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var other = (FormatInformation)o;
|
||||
return errorCorrectionLevel == other.errorCorrectionLevel && dataMask == other.dataMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
212
Others/zxing.core/zxing.core/qrcode/decoder/Mode.cs
Normal file
212
Others/zxing.core/zxing.core/qrcode/decoder/Mode.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
|
||||
/// data can be encoded to bits in the QR code standard.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public sealed class Mode
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public Names Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// enumeration for encoding modes
|
||||
/// </summary>
|
||||
public enum Names
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
TERMINATOR,
|
||||
/// <summary>
|
||||
/// numeric encoding
|
||||
/// </summary>
|
||||
NUMERIC,
|
||||
/// <summary>
|
||||
/// alpha-numeric encoding
|
||||
/// </summary>
|
||||
ALPHANUMERIC,
|
||||
/// <summary>
|
||||
/// structured append
|
||||
/// </summary>
|
||||
STRUCTURED_APPEND,
|
||||
/// <summary>
|
||||
/// byte mode encoding
|
||||
/// </summary>
|
||||
BYTE,
|
||||
/// <summary>
|
||||
/// ECI segment
|
||||
/// </summary>
|
||||
ECI,
|
||||
/// <summary>
|
||||
/// Kanji mode
|
||||
/// </summary>
|
||||
KANJI,
|
||||
/// <summary>
|
||||
/// FNC1 char, first position
|
||||
/// </summary>
|
||||
FNC1_FIRST_POSITION,
|
||||
/// <summary>
|
||||
/// FNC1 char, second position
|
||||
/// </summary>
|
||||
FNC1_SECOND_POSITION,
|
||||
/// <summary>
|
||||
/// Hanzi mode
|
||||
/// </summary>
|
||||
HANZI
|
||||
}
|
||||
|
||||
// No, we can't use an enum here. J2ME doesn't support it.
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode TERMINATOR = new Mode(new int[] { 0, 0, 0 }, 0x00, Names.TERMINATOR); // Not really a mode...
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode NUMERIC = new Mode(new int[] { 10, 12, 14 }, 0x01, Names.NUMERIC);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode ALPHANUMERIC = new Mode(new int[] { 9, 11, 13 }, 0x02, Names.ALPHANUMERIC);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode STRUCTURED_APPEND = new Mode(new int[] { 0, 0, 0 }, 0x03, Names.STRUCTURED_APPEND); // Not supported
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode BYTE = new Mode(new int[] { 8, 16, 16 }, 0x04, Names.BYTE);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode ECI = new Mode(null, 0x07, Names.ECI); // character counts don't apply
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode KANJI = new Mode(new int[] { 8, 10, 12 }, 0x08, Names.KANJI);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, Names.FNC1_FIRST_POSITION);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static readonly Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, Names.FNC1_SECOND_POSITION);
|
||||
/// <summary>See GBT 18284-2000; "Hanzi" is a transliteration of this mode name.</summary>
|
||||
public static readonly Mode HANZI = new Mode(new int[] { 8, 10, 12 }, 0x0D, Names.HANZI);
|
||||
|
||||
private readonly int[] characterCountBitsForVersions;
|
||||
|
||||
private Mode(int[] characterCountBitsForVersions, int bits, Names name)
|
||||
{
|
||||
this.characterCountBitsForVersions = characterCountBitsForVersions;
|
||||
Bits = bits;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fors the bits.
|
||||
/// </summary>
|
||||
/// <param name="bits">four bits encoding a QR Code data mode</param>
|
||||
/// <returns>
|
||||
/// <see cref="Mode"/> encoded by these bits
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">if bits do not correspond to a known mode</exception>
|
||||
public static Mode forBits(int bits)
|
||||
{
|
||||
switch (bits)
|
||||
{
|
||||
case 0x0:
|
||||
return TERMINATOR;
|
||||
case 0x1:
|
||||
return NUMERIC;
|
||||
case 0x2:
|
||||
return ALPHANUMERIC;
|
||||
case 0x3:
|
||||
return STRUCTURED_APPEND;
|
||||
case 0x4:
|
||||
return BYTE;
|
||||
case 0x5:
|
||||
return FNC1_FIRST_POSITION;
|
||||
case 0x7:
|
||||
return ECI;
|
||||
case 0x8:
|
||||
return KANJI;
|
||||
case 0x9:
|
||||
return FNC1_SECOND_POSITION;
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return HANZI;
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="version">version in question
|
||||
/// </param>
|
||||
/// <returns> number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
/// count of characters that will follow encoded in this {@link Mode}
|
||||
/// </returns>
|
||||
public int getCharacterCountBits(Version version)
|
||||
{
|
||||
if (characterCountBitsForVersions == null)
|
||||
{
|
||||
throw new ArgumentException("Character count doesn't apply to this mode");
|
||||
}
|
||||
int number = version.VersionNumber;
|
||||
int offset;
|
||||
if (number <= 9)
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
else if (number <= 26)
|
||||
{
|
||||
offset = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 2;
|
||||
}
|
||||
return characterCountBitsForVersions[offset];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits.
|
||||
/// </summary>
|
||||
public int Bits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String"/> that represents this instance.
|
||||
/// </returns>
|
||||
public override String ToString()
|
||||
{
|
||||
return Name.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the
|
||||
/// decoding caller. Callers are expected to process this.
|
||||
/// </summary>
|
||||
public sealed class QRCodeDecoderMetaData
|
||||
{
|
||||
private readonly bool mirrored;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QRCodeDecoderMetaData"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mirrored">if set to <c>true</c> [mirrored].</param>
|
||||
public QRCodeDecoderMetaData(bool mirrored)
|
||||
{
|
||||
this.mirrored = mirrored;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if the QR Code was mirrored.
|
||||
/// </summary>
|
||||
public bool IsMirrored
|
||||
{
|
||||
get { return mirrored; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the result points' order correction due to mirroring.
|
||||
/// </summary>
|
||||
/// <param name="points">Array of points to apply mirror correction to.</param>
|
||||
public void applyMirroredCorrection(ResultPoint[] points)
|
||||
{
|
||||
if (!mirrored || points == null || points.Length < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ResultPoint bottomLeft = points[0];
|
||||
points[0] = points[2];
|
||||
points[2] = bottomLeft;
|
||||
// No need to 'fix' top-left and alignment pattern.
|
||||
}
|
||||
}
|
||||
}
|
||||
684
Others/zxing.core/zxing.core/qrcode/decoder/Version.cs
Normal file
684
Others/zxing.core/zxing.core/qrcode/decoder/Version.cs
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* Copyright 2007 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 ZXing.Common;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// See ISO 18004:2006 Annex D
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public sealed class Version
|
||||
{
|
||||
/// <summary> See ISO 18004:2006 Annex D.
|
||||
/// Element i represents the raw version bits that specify version i + 7
|
||||
/// </summary>
|
||||
private static readonly int[] VERSION_DECODE_INFO = new[]
|
||||
{
|
||||
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
|
||||
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
|
||||
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
|
||||
0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
|
||||
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
|
||||
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
|
||||
0x2542E, 0x26A64, 0x27541, 0x28C69
|
||||
};
|
||||
|
||||
private static readonly Version[] VERSIONS = buildVersions();
|
||||
|
||||
private readonly int versionNumber;
|
||||
private readonly int[] alignmentPatternCenters;
|
||||
private readonly ECBlocks[] ecBlocks;
|
||||
private readonly int totalCodewords;
|
||||
|
||||
private Version(int versionNumber, int[] alignmentPatternCenters, params ECBlocks[] ecBlocks)
|
||||
{
|
||||
this.versionNumber = versionNumber;
|
||||
this.alignmentPatternCenters = alignmentPatternCenters;
|
||||
this.ecBlocks = ecBlocks;
|
||||
int total = 0;
|
||||
int ecCodewords = ecBlocks[0].ECCodewordsPerBlock;
|
||||
ECB[] ecbArray = ecBlocks[0].getECBlocks();
|
||||
foreach (var ecBlock in ecbArray)
|
||||
{
|
||||
total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords);
|
||||
}
|
||||
this.totalCodewords = total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version number.
|
||||
/// </summary>
|
||||
public int VersionNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alignment pattern centers.
|
||||
/// </summary>
|
||||
public int[] AlignmentPatternCenters
|
||||
{
|
||||
get
|
||||
{
|
||||
return alignmentPatternCenters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total codewords.
|
||||
/// </summary>
|
||||
public int TotalCodewords
|
||||
{
|
||||
get
|
||||
{
|
||||
return totalCodewords;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dimension for version.
|
||||
/// </summary>
|
||||
public int DimensionForVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return 17 + 4 * versionNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the EC blocks for level.
|
||||
/// </summary>
|
||||
/// <param name="ecLevel">The ec level.</param>
|
||||
/// <returns></returns>
|
||||
public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel)
|
||||
{
|
||||
return ecBlocks[ecLevel.ordinal()];
|
||||
}
|
||||
|
||||
/// <summary> <p>Deduces version information purely from QR Code dimensions.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dimension">dimension in modules
|
||||
/// </param>
|
||||
/// <returns><see cref="Version" /> for a QR Code of that dimension or null</returns>
|
||||
public static Version getProvisionalVersionForDimension(int dimension)
|
||||
{
|
||||
if (dimension % 4 != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return getVersionForNumber((dimension - 17) >> 2);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version for number.
|
||||
/// </summary>
|
||||
/// <param name="versionNumber">The version number.</param>
|
||||
/// <returns></returns>
|
||||
public static Version getVersionForNumber(int versionNumber)
|
||||
{
|
||||
if (versionNumber < 1 || versionNumber > 40)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
return VERSIONS[versionNumber - 1];
|
||||
}
|
||||
|
||||
internal static Version decodeVersionInformation(int versionBits)
|
||||
{
|
||||
int bestDifference = Int32.MaxValue;
|
||||
int bestVersion = 0;
|
||||
for (int i = 0; i < VERSION_DECODE_INFO.Length; i++)
|
||||
{
|
||||
int targetVersion = VERSION_DECODE_INFO[i];
|
||||
// Do the version info bits match exactly? done.
|
||||
if (targetVersion == versionBits)
|
||||
{
|
||||
return getVersionForNumber(i + 7);
|
||||
}
|
||||
// Otherwise see if this is the closest to a real version info bit string
|
||||
// we have seen so far
|
||||
int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
|
||||
if (bitsDifference < bestDifference)
|
||||
{
|
||||
bestVersion = i + 7;
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
}
|
||||
// We can tolerate up to 3 bits of error since no two version info codewords will
|
||||
// differ in less than 8 bits.
|
||||
if (bestDifference <= 3)
|
||||
{
|
||||
return getVersionForNumber(bestVersion);
|
||||
}
|
||||
// If we didn't find a close enough match, fail
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> See ISO 18004:2006 Annex E</summary>
|
||||
internal BitMatrix buildFunctionPattern()
|
||||
{
|
||||
int dimension = DimensionForVersion;
|
||||
BitMatrix bitMatrix = new BitMatrix(dimension);
|
||||
|
||||
// Top left finder pattern + separator + format
|
||||
bitMatrix.setRegion(0, 0, 9, 9);
|
||||
// Top right finder pattern + separator + format
|
||||
bitMatrix.setRegion(dimension - 8, 0, 8, 9);
|
||||
// Bottom left finder pattern + separator + format
|
||||
bitMatrix.setRegion(0, dimension - 8, 9, 8);
|
||||
|
||||
// Alignment patterns
|
||||
int max = alignmentPatternCenters.Length;
|
||||
for (int x = 0; x < max; x++)
|
||||
{
|
||||
int i = alignmentPatternCenters[x] - 2;
|
||||
for (int y = 0; y < max; y++)
|
||||
{
|
||||
if ((x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0))
|
||||
{
|
||||
bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
|
||||
}
|
||||
// else no o alignment patterns near the three finder patterns
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical timing pattern
|
||||
bitMatrix.setRegion(6, 9, 1, dimension - 17);
|
||||
// Horizontal timing pattern
|
||||
bitMatrix.setRegion(9, 6, dimension - 17, 1);
|
||||
|
||||
if (versionNumber > 6)
|
||||
{
|
||||
// Version info, top right
|
||||
bitMatrix.setRegion(dimension - 11, 0, 3, 6);
|
||||
// Version info, bottom left
|
||||
bitMatrix.setRegion(0, dimension - 11, 6, 3);
|
||||
}
|
||||
|
||||
return bitMatrix;
|
||||
}
|
||||
|
||||
/// <summary> <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||
/// use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||
/// each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||
/// will be the same across all blocks within one version.</p>
|
||||
/// </summary>
|
||||
public sealed class ECBlocks
|
||||
{
|
||||
private readonly int ecCodewordsPerBlock;
|
||||
private readonly ECB[] ecBlocks;
|
||||
|
||||
internal ECBlocks(int ecCodewordsPerBlock, params ECB[] ecBlocks)
|
||||
{
|
||||
this.ecCodewordsPerBlock = ecCodewordsPerBlock;
|
||||
this.ecBlocks = ecBlocks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the EC codewords per block.
|
||||
/// </summary>
|
||||
public int ECCodewordsPerBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return ecCodewordsPerBlock;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the num blocks.
|
||||
/// </summary>
|
||||
public int NumBlocks
|
||||
{
|
||||
get
|
||||
{
|
||||
int total = 0;
|
||||
foreach (var ecBlock in ecBlocks)
|
||||
{
|
||||
total += ecBlock.Count;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total EC codewords.
|
||||
/// </summary>
|
||||
public int TotalECCodewords
|
||||
{
|
||||
get
|
||||
{
|
||||
return ecCodewordsPerBlock * NumBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the EC blocks.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ECB[] getECBlocks()
|
||||
{
|
||||
return ecBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> <p>Encapsulates the parameters for one error-correction block in one symbol version.
|
||||
/// This includes the number of data codewords, and the number of times a block with these
|
||||
/// parameters is used consecutively in the QR code version's format.</p>
|
||||
/// </summary>
|
||||
public sealed class ECB
|
||||
{
|
||||
private readonly int count;
|
||||
private readonly int dataCodewords;
|
||||
|
||||
internal ECB(int count, int dataCodewords)
|
||||
{
|
||||
this.count = count;
|
||||
this.dataCodewords = dataCodewords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the data codewords.
|
||||
/// </summary>
|
||||
public int DataCodewords
|
||||
{
|
||||
get
|
||||
{
|
||||
return dataCodewords;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String"/> that represents this instance.
|
||||
/// </returns>
|
||||
public override String ToString()
|
||||
{
|
||||
return Convert.ToString(versionNumber);
|
||||
}
|
||||
|
||||
/// <summary> See ISO 18004:2006 6.5.1 Table 9</summary>
|
||||
private static Version[] buildVersions()
|
||||
{
|
||||
return new Version[]
|
||||
{
|
||||
new Version(1, new int[] {},
|
||||
new ECBlocks(7, new ECB(1, 19)),
|
||||
new ECBlocks(10, new ECB(1, 16)),
|
||||
new ECBlocks(13, new ECB(1, 13)),
|
||||
new ECBlocks(17, new ECB(1, 9))),
|
||||
new Version(2, new int[] {6, 18},
|
||||
new ECBlocks(10, new ECB(1, 34)),
|
||||
new ECBlocks(16, new ECB(1, 28)),
|
||||
new ECBlocks(22, new ECB(1, 22)),
|
||||
new ECBlocks(28, new ECB(1, 16))),
|
||||
new Version(3, new int[] {6, 22},
|
||||
new ECBlocks(15, new ECB(1, 55)),
|
||||
new ECBlocks(26, new ECB(1, 44)),
|
||||
new ECBlocks(18, new ECB(2, 17)),
|
||||
new ECBlocks(22, new ECB(2, 13))),
|
||||
new Version(4, new int[] {6, 26},
|
||||
new ECBlocks(20, new ECB(1, 80)),
|
||||
new ECBlocks(18, new ECB(2, 32)),
|
||||
new ECBlocks(26, new ECB(2, 24)),
|
||||
new ECBlocks(16, new ECB(4, 9))),
|
||||
new Version(5, new int[] {6, 30},
|
||||
new ECBlocks(26, new ECB(1, 108)),
|
||||
new ECBlocks(24, new ECB(2, 43)),
|
||||
new ECBlocks(18, new ECB(2, 15),
|
||||
new ECB(2, 16)),
|
||||
new ECBlocks(22, new ECB(2, 11),
|
||||
new ECB(2, 12))),
|
||||
new Version(6, new int[] {6, 34},
|
||||
new ECBlocks(18, new ECB(2, 68)),
|
||||
new ECBlocks(16, new ECB(4, 27)),
|
||||
new ECBlocks(24, new ECB(4, 19)),
|
||||
new ECBlocks(28, new ECB(4, 15))),
|
||||
new Version(7, new int[] {6, 22, 38},
|
||||
new ECBlocks(20, new ECB(2, 78)),
|
||||
new ECBlocks(18, new ECB(4, 31)),
|
||||
new ECBlocks(18, new ECB(2, 14),
|
||||
new ECB(4, 15)),
|
||||
new ECBlocks(26, new ECB(4, 13),
|
||||
new ECB(1, 14))),
|
||||
new Version(8, new int[] {6, 24, 42},
|
||||
new ECBlocks(24, new ECB(2, 97)),
|
||||
new ECBlocks(22, new ECB(2, 38),
|
||||
new ECB(2, 39)),
|
||||
new ECBlocks(22, new ECB(4, 18),
|
||||
new ECB(2, 19)),
|
||||
new ECBlocks(26, new ECB(4, 14),
|
||||
new ECB(2, 15))),
|
||||
new Version(9, new int[] {6, 26, 46},
|
||||
new ECBlocks(30, new ECB(2, 116)),
|
||||
new ECBlocks(22, new ECB(3, 36),
|
||||
new ECB(2, 37)),
|
||||
new ECBlocks(20, new ECB(4, 16),
|
||||
new ECB(4, 17)),
|
||||
new ECBlocks(24, new ECB(4, 12),
|
||||
new ECB(4, 13))),
|
||||
new Version(10, new int[] {6, 28, 50},
|
||||
new ECBlocks(18, new ECB(2, 68),
|
||||
new ECB(2, 69)),
|
||||
new ECBlocks(26, new ECB(4, 43),
|
||||
new ECB(1, 44)),
|
||||
new ECBlocks(24, new ECB(6, 19),
|
||||
new ECB(2, 20)),
|
||||
new ECBlocks(28, new ECB(6, 15),
|
||||
new ECB(2, 16))),
|
||||
new Version(11, new int[] {6, 30, 54},
|
||||
new ECBlocks(20, new ECB(4, 81)),
|
||||
new ECBlocks(30, new ECB(1, 50),
|
||||
new ECB(4, 51)),
|
||||
new ECBlocks(28, new ECB(4, 22),
|
||||
new ECB(4, 23)),
|
||||
new ECBlocks(24, new ECB(3, 12),
|
||||
new ECB(8, 13))),
|
||||
new Version(12, new int[] {6, 32, 58},
|
||||
new ECBlocks(24, new ECB(2, 92),
|
||||
new ECB(2, 93)),
|
||||
new ECBlocks(22, new ECB(6, 36),
|
||||
new ECB(2, 37)),
|
||||
new ECBlocks(26, new ECB(4, 20),
|
||||
new ECB(6, 21)),
|
||||
new ECBlocks(28, new ECB(7, 14),
|
||||
new ECB(4, 15))),
|
||||
new Version(13, new int[] {6, 34, 62},
|
||||
new ECBlocks(26, new ECB(4, 107)),
|
||||
new ECBlocks(22, new ECB(8, 37),
|
||||
new ECB(1, 38)),
|
||||
new ECBlocks(24, new ECB(8, 20),
|
||||
new ECB(4, 21)),
|
||||
new ECBlocks(22, new ECB(12, 11),
|
||||
new ECB(4, 12))),
|
||||
new Version(14, new int[] {6, 26, 46, 66},
|
||||
new ECBlocks(30, new ECB(3, 115),
|
||||
new ECB(1, 116)),
|
||||
new ECBlocks(24, new ECB(4, 40),
|
||||
new ECB(5, 41)),
|
||||
new ECBlocks(20, new ECB(11, 16),
|
||||
new ECB(5, 17)),
|
||||
new ECBlocks(24, new ECB(11, 12),
|
||||
new ECB(5, 13))),
|
||||
new Version(15, new int[] {6, 26, 48, 70},
|
||||
new ECBlocks(22, new ECB(5, 87),
|
||||
new ECB(1, 88)),
|
||||
new ECBlocks(24, new ECB(5, 41),
|
||||
new ECB(5, 42)),
|
||||
new ECBlocks(30, new ECB(5, 24),
|
||||
new ECB(7, 25)),
|
||||
new ECBlocks(24, new ECB(11, 12),
|
||||
new ECB(7, 13))),
|
||||
new Version(16, new int[] {6, 26, 50, 74},
|
||||
new ECBlocks(24, new ECB(5, 98),
|
||||
new ECB(1, 99)),
|
||||
new ECBlocks(28, new ECB(7, 45),
|
||||
new ECB(3, 46)),
|
||||
new ECBlocks(24, new ECB(15, 19),
|
||||
new ECB(2, 20)),
|
||||
new ECBlocks(30, new ECB(3, 15),
|
||||
new ECB(13, 16))),
|
||||
new Version(17, new int[] {6, 30, 54, 78},
|
||||
new ECBlocks(28, new ECB(1, 107),
|
||||
new ECB(5, 108)),
|
||||
new ECBlocks(28, new ECB(10, 46),
|
||||
new ECB(1, 47)),
|
||||
new ECBlocks(28, new ECB(1, 22),
|
||||
new ECB(15, 23)),
|
||||
new ECBlocks(28, new ECB(2, 14),
|
||||
new ECB(17, 15))),
|
||||
new Version(18, new int[] {6, 30, 56, 82},
|
||||
new ECBlocks(30, new ECB(5, 120),
|
||||
new ECB(1, 121)),
|
||||
new ECBlocks(26, new ECB(9, 43),
|
||||
new ECB(4, 44)),
|
||||
new ECBlocks(28, new ECB(17, 22),
|
||||
new ECB(1, 23)),
|
||||
new ECBlocks(28, new ECB(2, 14),
|
||||
new ECB(19, 15))),
|
||||
new Version(19, new int[] {6, 30, 58, 86},
|
||||
new ECBlocks(28, new ECB(3, 113),
|
||||
new ECB(4, 114)),
|
||||
new ECBlocks(26, new ECB(3, 44),
|
||||
new ECB(11, 45)),
|
||||
new ECBlocks(26, new ECB(17, 21),
|
||||
new ECB(4, 22)),
|
||||
new ECBlocks(26, new ECB(9, 13),
|
||||
new ECB(16, 14))),
|
||||
new Version(20, new int[] {6, 34, 62, 90},
|
||||
new ECBlocks(28, new ECB(3, 107),
|
||||
new ECB(5, 108)),
|
||||
new ECBlocks(26, new ECB(3, 41),
|
||||
new ECB(13, 42)),
|
||||
new ECBlocks(30, new ECB(15, 24),
|
||||
new ECB(5, 25)),
|
||||
new ECBlocks(28, new ECB(15, 15),
|
||||
new ECB(10, 16))),
|
||||
new Version(21, new int[] {6, 28, 50, 72, 94},
|
||||
new ECBlocks(28, new ECB(4, 116),
|
||||
new ECB(4, 117)),
|
||||
new ECBlocks(26, new ECB(17, 42)),
|
||||
new ECBlocks(28, new ECB(17, 22),
|
||||
new ECB(6, 23)),
|
||||
new ECBlocks(30, new ECB(19, 16),
|
||||
new ECB(6, 17))),
|
||||
new Version(22, new int[] {6, 26, 50, 74, 98},
|
||||
new ECBlocks(28, new ECB(2, 111),
|
||||
new ECB(7, 112)),
|
||||
new ECBlocks(28, new ECB(17, 46)),
|
||||
new ECBlocks(30, new ECB(7, 24),
|
||||
new ECB(16, 25)),
|
||||
new ECBlocks(24, new ECB(34, 13))),
|
||||
new Version(23, new int[] {6, 30, 54, 78, 102},
|
||||
new ECBlocks(30, new ECB(4, 121),
|
||||
new ECB(5, 122)),
|
||||
new ECBlocks(28, new ECB(4, 47),
|
||||
new ECB(14, 48)),
|
||||
new ECBlocks(30, new ECB(11, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(16, 15),
|
||||
new ECB(14, 16))),
|
||||
new Version(24, new int[] {6, 28, 54, 80, 106},
|
||||
new ECBlocks(30, new ECB(6, 117),
|
||||
new ECB(4, 118)),
|
||||
new ECBlocks(28, new ECB(6, 45),
|
||||
new ECB(14, 46)),
|
||||
new ECBlocks(30, new ECB(11, 24),
|
||||
new ECB(16, 25)),
|
||||
new ECBlocks(30, new ECB(30, 16),
|
||||
new ECB(2, 17))),
|
||||
new Version(25, new int[] {6, 32, 58, 84, 110},
|
||||
new ECBlocks(26, new ECB(8, 106),
|
||||
new ECB(4, 107)),
|
||||
new ECBlocks(28, new ECB(8, 47),
|
||||
new ECB(13, 48)),
|
||||
new ECBlocks(30, new ECB(7, 24),
|
||||
new ECB(22, 25)),
|
||||
new ECBlocks(30, new ECB(22, 15),
|
||||
new ECB(13, 16))),
|
||||
new Version(26, new int[] {6, 30, 58, 86, 114},
|
||||
new ECBlocks(28, new ECB(10, 114),
|
||||
new ECB(2, 115)),
|
||||
new ECBlocks(28, new ECB(19, 46),
|
||||
new ECB(4, 47)),
|
||||
new ECBlocks(28, new ECB(28, 22),
|
||||
new ECB(6, 23)),
|
||||
new ECBlocks(30, new ECB(33, 16),
|
||||
new ECB(4, 17))),
|
||||
new Version(27, new int[] {6, 34, 62, 90, 118},
|
||||
new ECBlocks(30, new ECB(8, 122),
|
||||
new ECB(4, 123)),
|
||||
new ECBlocks(28, new ECB(22, 45),
|
||||
new ECB(3, 46)),
|
||||
new ECBlocks(30, new ECB(8, 23),
|
||||
new ECB(26, 24)),
|
||||
new ECBlocks(30, new ECB(12, 15),
|
||||
new ECB(28, 16))),
|
||||
new Version(28, new int[] {6, 26, 50, 74, 98, 122},
|
||||
new ECBlocks(30, new ECB(3, 117),
|
||||
new ECB(10, 118)),
|
||||
new ECBlocks(28, new ECB(3, 45),
|
||||
new ECB(23, 46)),
|
||||
new ECBlocks(30, new ECB(4, 24),
|
||||
new ECB(31, 25)),
|
||||
new ECBlocks(30, new ECB(11, 15),
|
||||
new ECB(31, 16))),
|
||||
new Version(29, new int[] {6, 30, 54, 78, 102, 126},
|
||||
new ECBlocks(30, new ECB(7, 116),
|
||||
new ECB(7, 117)),
|
||||
new ECBlocks(28, new ECB(21, 45),
|
||||
new ECB(7, 46)),
|
||||
new ECBlocks(30, new ECB(1, 23),
|
||||
new ECB(37, 24)),
|
||||
new ECBlocks(30, new ECB(19, 15),
|
||||
new ECB(26, 16))),
|
||||
new Version(30, new int[] {6, 26, 52, 78, 104, 130},
|
||||
new ECBlocks(30, new ECB(5, 115),
|
||||
new ECB(10, 116)),
|
||||
new ECBlocks(28, new ECB(19, 47),
|
||||
new ECB(10, 48)),
|
||||
new ECBlocks(30, new ECB(15, 24),
|
||||
new ECB(25, 25)),
|
||||
new ECBlocks(30, new ECB(23, 15),
|
||||
new ECB(25, 16))),
|
||||
new Version(31, new int[] {6, 30, 56, 82, 108, 134},
|
||||
new ECBlocks(30, new ECB(13, 115),
|
||||
new ECB(3, 116)),
|
||||
new ECBlocks(28, new ECB(2, 46),
|
||||
new ECB(29, 47)),
|
||||
new ECBlocks(30, new ECB(42, 24),
|
||||
new ECB(1, 25)),
|
||||
new ECBlocks(30, new ECB(23, 15),
|
||||
new ECB(28, 16))),
|
||||
new Version(32, new int[] {6, 34, 60, 86, 112, 138},
|
||||
new ECBlocks(30, new ECB(17, 115)),
|
||||
new ECBlocks(28, new ECB(10, 46),
|
||||
new ECB(23, 47)),
|
||||
new ECBlocks(30, new ECB(10, 24),
|
||||
new ECB(35, 25)),
|
||||
new ECBlocks(30, new ECB(19, 15),
|
||||
new ECB(35, 16))),
|
||||
new Version(33, new int[] {6, 30, 58, 86, 114, 142},
|
||||
new ECBlocks(30, new ECB(17, 115),
|
||||
new ECB(1, 116)),
|
||||
new ECBlocks(28, new ECB(14, 46),
|
||||
new ECB(21, 47)),
|
||||
new ECBlocks(30, new ECB(29, 24),
|
||||
new ECB(19, 25)),
|
||||
new ECBlocks(30, new ECB(11, 15),
|
||||
new ECB(46, 16))),
|
||||
new Version(34, new int[] {6, 34, 62, 90, 118, 146},
|
||||
new ECBlocks(30, new ECB(13, 115),
|
||||
new ECB(6, 116)),
|
||||
new ECBlocks(28, new ECB(14, 46),
|
||||
new ECB(23, 47)),
|
||||
new ECBlocks(30, new ECB(44, 24),
|
||||
new ECB(7, 25)),
|
||||
new ECBlocks(30, new ECB(59, 16),
|
||||
new ECB(1, 17))),
|
||||
new Version(35, new int[] {6, 30, 54, 78, 102, 126, 150},
|
||||
new ECBlocks(30, new ECB(12, 121),
|
||||
new ECB(7, 122)),
|
||||
new ECBlocks(28, new ECB(12, 47),
|
||||
new ECB(26, 48)),
|
||||
new ECBlocks(30, new ECB(39, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(22, 15),
|
||||
new ECB(41, 16))),
|
||||
new Version(36, new int[] {6, 24, 50, 76, 102, 128, 154},
|
||||
new ECBlocks(30, new ECB(6, 121),
|
||||
new ECB(14, 122)),
|
||||
new ECBlocks(28, new ECB(6, 47),
|
||||
new ECB(34, 48)),
|
||||
new ECBlocks(30, new ECB(46, 24),
|
||||
new ECB(10, 25)),
|
||||
new ECBlocks(30, new ECB(2, 15),
|
||||
new ECB(64, 16))),
|
||||
new Version(37, new int[] {6, 28, 54, 80, 106, 132, 158},
|
||||
new ECBlocks(30, new ECB(17, 122),
|
||||
new ECB(4, 123)),
|
||||
new ECBlocks(28, new ECB(29, 46),
|
||||
new ECB(14, 47)),
|
||||
new ECBlocks(30, new ECB(49, 24),
|
||||
new ECB(10, 25)),
|
||||
new ECBlocks(30, new ECB(24, 15),
|
||||
new ECB(46, 16))),
|
||||
new Version(38, new int[] {6, 32, 58, 84, 110, 136, 162},
|
||||
new ECBlocks(30, new ECB(4, 122),
|
||||
new ECB(18, 123)),
|
||||
new ECBlocks(28, new ECB(13, 46),
|
||||
new ECB(32, 47)),
|
||||
new ECBlocks(30, new ECB(48, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(42, 15),
|
||||
new ECB(32, 16))),
|
||||
new Version(39, new int[] {6, 26, 54, 82, 110, 138, 166},
|
||||
new ECBlocks(30, new ECB(20, 117),
|
||||
new ECB(4, 118)),
|
||||
new ECBlocks(28, new ECB(40, 47),
|
||||
new ECB(7, 48)),
|
||||
new ECBlocks(30, new ECB(43, 24),
|
||||
new ECB(22, 25)),
|
||||
new ECBlocks(30, new ECB(10, 15),
|
||||
new ECB(67, 16))),
|
||||
new Version(40, new int[] {6, 30, 58, 86, 114, 142, 170},
|
||||
new ECBlocks(30, new ECB(19, 118),
|
||||
new ECB(6, 119)),
|
||||
new ECBlocks(28, new ECB(18, 47),
|
||||
new ECB(31, 48)),
|
||||
new ECBlocks(30, new ECB(34, 24),
|
||||
new ECB(34, 25)),
|
||||
new ECBlocks(30, new ECB(20, 15),
|
||||
new ECB(61, 16)))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary> <p>Encapsulates an alignment pattern, which are the smaller square patterns found in
|
||||
/// all but the simplest QR Codes.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author> Sean Owen
|
||||
/// </author>
|
||||
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
|
||||
/// </author>
|
||||
public sealed class AlignmentPattern : ResultPoint
|
||||
{
|
||||
private float estimatedModuleSize;
|
||||
|
||||
internal AlignmentPattern(float posX, float posY, float estimatedModuleSize)
|
||||
: base(posX, posY)
|
||||
{
|
||||
this.estimatedModuleSize = estimatedModuleSize;
|
||||
}
|
||||
|
||||
/// <summary> <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
|
||||
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
/// </summary>
|
||||
internal bool aboutEquals(float moduleSize, float i, float j)
|
||||
{
|
||||
if (Math.Abs(i - Y) <= moduleSize && Math.Abs(j - X) <= moduleSize)
|
||||
{
|
||||
float moduleSizeDiff = Math.Abs(moduleSize - estimatedModuleSize);
|
||||
return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines this object's current estimate of a finder pattern position and module size
|
||||
/// with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
|
||||
/// </summary>
|
||||
/// <param name="i">The i.</param>
|
||||
/// <param name="j">The j.</param>
|
||||
/// <param name="newModuleSize">New size of the module.</param>
|
||||
/// <returns></returns>
|
||||
internal AlignmentPattern combineEstimate(float i, float j, float newModuleSize)
|
||||
{
|
||||
float combinedX = (X + j) / 2.0f;
|
||||
float combinedY = (Y + i) / 2.0f;
|
||||
float combinedModuleSize = (estimatedModuleSize + newModuleSize) / 2.0f;
|
||||
return new AlignmentPattern(combinedX, combinedY, combinedModuleSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary> <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
|
||||
/// patterns but are smaller and appear at regular intervals throughout the image.</p>
|
||||
///
|
||||
/// <p>At the moment this only looks for the bottom-right alignment pattern.</p>
|
||||
///
|
||||
/// <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
|
||||
/// pasted and stripped down here for maximum performance but does unfortunately duplicate
|
||||
/// some code.</p>
|
||||
///
|
||||
/// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author> Sean Owen
|
||||
/// </author>
|
||||
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
|
||||
/// </author>
|
||||
sealed class AlignmentPatternFinder
|
||||
{
|
||||
private readonly BitMatrix image;
|
||||
private readonly IList<AlignmentPattern> possibleCenters;
|
||||
private readonly int startX;
|
||||
private readonly int startY;
|
||||
private readonly int width;
|
||||
private readonly int height;
|
||||
private readonly float moduleSize;
|
||||
private readonly int[] crossCheckStateCount;
|
||||
private readonly ResultPointCallback resultPointCallback;
|
||||
|
||||
/// <summary> <p>Creates a finder that will look in a portion of the whole image.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="image">image to search
|
||||
/// </param>
|
||||
/// <param name="startX">left column from which to start searching
|
||||
/// </param>
|
||||
/// <param name="startY">top row from which to start searching
|
||||
/// </param>
|
||||
/// <param name="width">width of region to search
|
||||
/// </param>
|
||||
/// <param name="height">height of region to search
|
||||
/// </param>
|
||||
/// <param name="moduleSize">estimated module size so far
|
||||
/// </param>
|
||||
/// <param name="resultPointCallback">callback function which is called, when a result point is found</param>
|
||||
internal AlignmentPatternFinder(BitMatrix image, int startX, int startY, int width, int height, float moduleSize, ResultPointCallback resultPointCallback)
|
||||
{
|
||||
this.image = image;
|
||||
this.possibleCenters = new List<AlignmentPattern>(5);
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.moduleSize = moduleSize;
|
||||
this.crossCheckStateCount = new int[3];
|
||||
this.resultPointCallback = resultPointCallback;
|
||||
}
|
||||
|
||||
/// <summary> <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
|
||||
/// it's pretty performance-critical and so is written to be fast foremost.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns><see cref="AlignmentPattern"/> if found</returns>
|
||||
internal AlignmentPattern find()
|
||||
{
|
||||
int startX = this.startX;
|
||||
int height = this.height;
|
||||
int maxJ = startX + width;
|
||||
int middleI = startY + (height >> 1);
|
||||
// We are looking for black/white/black modules in 1:1:1 ratio;
|
||||
// this tracks the number of black/white/black modules seen so far
|
||||
int[] stateCount = new int[3];
|
||||
for (int iGen = 0; iGen < height; iGen++)
|
||||
{
|
||||
// Search from middle outwards
|
||||
int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
|
||||
stateCount[0] = 0;
|
||||
stateCount[1] = 0;
|
||||
stateCount[2] = 0;
|
||||
int j = startX;
|
||||
// Burn off leading white pixels before anything else; if we start in the middle of
|
||||
// a white run, it doesn't make sense to count its length, since we don't know if the
|
||||
// white run continued to the left of the start point
|
||||
while (j < maxJ && !image[j, i])
|
||||
{
|
||||
j++;
|
||||
}
|
||||
int currentState = 0;
|
||||
while (j < maxJ)
|
||||
{
|
||||
if (image[j, i])
|
||||
{
|
||||
// Black pixel
|
||||
if (currentState == 1)
|
||||
{
|
||||
// Counting black pixels
|
||||
stateCount[1]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counting white pixels
|
||||
if (currentState == 2)
|
||||
{
|
||||
// A winner?
|
||||
if (foundPatternCross(stateCount))
|
||||
{
|
||||
// Yes
|
||||
AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);
|
||||
if (confirmed != null)
|
||||
{
|
||||
return confirmed;
|
||||
}
|
||||
}
|
||||
stateCount[0] = stateCount[2];
|
||||
stateCount[1] = 1;
|
||||
stateCount[2] = 0;
|
||||
currentState = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateCount[++currentState]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// White pixel
|
||||
if (currentState == 1)
|
||||
{
|
||||
// Counting black pixels
|
||||
currentState++;
|
||||
}
|
||||
stateCount[currentState]++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if (foundPatternCross(stateCount))
|
||||
{
|
||||
AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);
|
||||
if (confirmed != null)
|
||||
{
|
||||
return confirmed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hmm, nothing we saw was observed and confirmed twice. If we had
|
||||
// any guess at all, return it.
|
||||
if (possibleCenters.Count != 0)
|
||||
{
|
||||
return possibleCenters[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> Given a count of black/white/black pixels just seen and an end position,
|
||||
/// figures the location of the center of this black/white/black run.
|
||||
/// </summary>
|
||||
private static float? centerFromEnd(int[] stateCount, int end)
|
||||
{
|
||||
var result = (end - stateCount[2]) - stateCount[1] / 2.0f;
|
||||
if (Single.IsNaN(result))
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <param name="stateCount">count of black/white/black pixels just read
|
||||
/// </param>
|
||||
/// <returns> true iff the proportions of the counts is close enough to the 1/1/1 ratios
|
||||
/// used by alignment patterns to be considered a match
|
||||
/// </returns>
|
||||
private bool foundPatternCross(int[] stateCount)
|
||||
{
|
||||
float maxVariance = moduleSize / 2.0f;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (Math.Abs(moduleSize - stateCount[i]) >= maxVariance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>After a horizontal scan finds a potential alignment pattern, this method
|
||||
/// "cross-checks" by scanning down vertically through the center of the possible
|
||||
/// alignment pattern to see if the same proportion is detected.</p>
|
||||
/// </summary>
|
||||
/// <param name="startI">row where an alignment pattern was detected</param>
|
||||
/// <param name="centerJ">center of the section that appears to cross an alignment pattern</param>
|
||||
/// <param name="maxCount">maximum reasonable number of modules that should be
|
||||
/// observed in any reading state, based on the results of the horizontal scan</param>
|
||||
/// <param name="originalStateCountTotal">The original state count total.</param>
|
||||
/// <returns>
|
||||
/// vertical center of alignment pattern, or null if not found
|
||||
/// </returns>
|
||||
private float? crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal)
|
||||
{
|
||||
int maxI = image.Height;
|
||||
int[] stateCount = crossCheckStateCount;
|
||||
stateCount[0] = 0;
|
||||
stateCount[1] = 0;
|
||||
stateCount[2] = 0;
|
||||
|
||||
// Start counting up from center
|
||||
int i = startI;
|
||||
while (i >= 0 && image[centerJ, i] && stateCount[1] <= maxCount)
|
||||
{
|
||||
stateCount[1]++;
|
||||
i--;
|
||||
}
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if (i < 0 || stateCount[1] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i >= 0 && !image[centerJ, i] && stateCount[0] <= maxCount)
|
||||
{
|
||||
stateCount[0]++;
|
||||
i--;
|
||||
}
|
||||
if (stateCount[0] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
i = startI + 1;
|
||||
while (i < maxI && image[centerJ, i] && stateCount[1] <= maxCount)
|
||||
{
|
||||
stateCount[1]++;
|
||||
i++;
|
||||
}
|
||||
if (i == maxI || stateCount[1] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i < maxI && !image[centerJ, i] && stateCount[2] <= maxCount)
|
||||
{
|
||||
stateCount[2]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[2] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
|
||||
if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : null;
|
||||
}
|
||||
|
||||
/// <summary> <p>This is called when a horizontal scan finds a possible alignment pattern. It will
|
||||
/// cross check with a vertical scan, and if successful, will see if this pattern had been
|
||||
/// found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
|
||||
/// found the alignment pattern.</p>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="stateCount">reading state module counts from horizontal scan
|
||||
/// </param>
|
||||
/// <param name="i">row where alignment pattern may be found
|
||||
/// </param>
|
||||
/// <param name="j">end of possible alignment pattern in row
|
||||
/// </param>
|
||||
/// <returns> {@link AlignmentPattern} if we have found the same pattern twice, or null if not
|
||||
/// </returns>
|
||||
private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j)
|
||||
{
|
||||
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
|
||||
float? centerJ = centerFromEnd(stateCount, j);
|
||||
if (centerJ == null)
|
||||
return null;
|
||||
float? centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal);
|
||||
if (centerI != null)
|
||||
{
|
||||
float estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
|
||||
foreach (var center in possibleCenters)
|
||||
{
|
||||
// Look for about the same center and module size:
|
||||
if (center.aboutEquals(estimatedModuleSize, centerI.Value, centerJ.Value))
|
||||
{
|
||||
return center.combineEstimate(centerI.Value, centerJ.Value, estimatedModuleSize);
|
||||
}
|
||||
}
|
||||
// Hadn't found this before; save it
|
||||
var point = new AlignmentPattern(centerJ.Value, centerI.Value, estimatedModuleSize);
|
||||
possibleCenters.Add(point);
|
||||
if (resultPointCallback != null)
|
||||
{
|
||||
resultPointCallback(point);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
434
Others/zxing.core/zxing.core/qrcode/detector/Detector.cs
Normal file
434
Others/zxing.core/zxing.core/qrcode/detector/Detector.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Copyright 2007 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.Common.Detector;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
|
||||
/// is rotated or skewed, or partially obscured.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public class Detector
|
||||
{
|
||||
private readonly BitMatrix image;
|
||||
private ResultPointCallback resultPointCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Detector"/> class.
|
||||
/// </summary>
|
||||
/// <param name="image">The image.</param>
|
||||
public Detector(BitMatrix image)
|
||||
{
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image.
|
||||
/// </summary>
|
||||
virtual protected internal BitMatrix Image
|
||||
{
|
||||
get
|
||||
{
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result point callback.
|
||||
/// </summary>
|
||||
virtual protected internal ResultPointCallback ResultPointCallback
|
||||
{
|
||||
get
|
||||
{
|
||||
return resultPointCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Detects a QR Code in an image.</p>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see cref="DetectorResult"/> encapsulating results of detecting a QR Code
|
||||
/// </returns>
|
||||
public virtual DetectorResult detect()
|
||||
{
|
||||
return detect(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Detects a QR Code in an image.</p>
|
||||
/// </summary>
|
||||
/// <param name="hints">optional hints to detector</param>
|
||||
/// <returns>
|
||||
/// <see cref="DetectorResult"/> encapsulating results of detecting a QR Code
|
||||
/// </returns>
|
||||
public virtual DetectorResult detect(IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
resultPointCallback = hints == null || !hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK) ? null : (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK];
|
||||
|
||||
FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);
|
||||
FinderPatternInfo info = finder.find(hints);
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
return processFinderPatternInfo(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the finder pattern info.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns></returns>
|
||||
protected internal virtual DetectorResult processFinderPatternInfo(FinderPatternInfo info)
|
||||
{
|
||||
FinderPattern topLeft = info.TopLeft;
|
||||
FinderPattern topRight = info.TopRight;
|
||||
FinderPattern bottomLeft = info.BottomLeft;
|
||||
|
||||
float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
|
||||
if (moduleSize < 1.0f)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int dimension;
|
||||
if (!computeDimension(topLeft, topRight, bottomLeft, moduleSize, out dimension))
|
||||
return null;
|
||||
Internal.Version provisionalVersion = Internal.Version.getProvisionalVersionForDimension(dimension);
|
||||
if (provisionalVersion == null)
|
||||
return null;
|
||||
int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7;
|
||||
|
||||
AlignmentPattern alignmentPattern = null;
|
||||
// Anything above version 1 has an alignment pattern
|
||||
if (provisionalVersion.AlignmentPatternCenters.Length > 0)
|
||||
{
|
||||
|
||||
// Guess where a "bottom right" finder pattern would have been
|
||||
float bottomRightX = topRight.X - topLeft.X + bottomLeft.X;
|
||||
float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;
|
||||
|
||||
// Estimate that alignment pattern is closer by 3 modules
|
||||
// from "bottom right" to known top left location
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters;
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
int estAlignmentX = (int)(topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X));
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
int estAlignmentY = (int)(topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y));
|
||||
|
||||
// Kind of arbitrary -- expand search radius before giving up
|
||||
for (int i = 4; i <= 16; i <<= 1)
|
||||
{
|
||||
alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i);
|
||||
if (alignmentPattern == null)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
// If we didn't find alignment pattern... well try anyway without it
|
||||
}
|
||||
|
||||
PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
|
||||
|
||||
BitMatrix bits = sampleGrid(image, transform, dimension);
|
||||
if (bits == null)
|
||||
return null;
|
||||
|
||||
ResultPoint[] points;
|
||||
if (alignmentPattern == null)
|
||||
{
|
||||
points = new ResultPoint[] { bottomLeft, topLeft, topRight };
|
||||
}
|
||||
else
|
||||
{
|
||||
points = new ResultPoint[] { bottomLeft, topLeft, topRight, alignmentPattern };
|
||||
}
|
||||
return new DetectorResult(bits, points);
|
||||
}
|
||||
|
||||
private static PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
float dimMinusThree = (float)dimension - 3.5f;
|
||||
float bottomRightX;
|
||||
float bottomRightY;
|
||||
float sourceBottomRightX;
|
||||
float sourceBottomRightY;
|
||||
if (alignmentPattern != null)
|
||||
{
|
||||
bottomRightX = alignmentPattern.X;
|
||||
bottomRightY = alignmentPattern.Y;
|
||||
sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't have an alignment pattern, just make up the bottom-right point
|
||||
bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X;
|
||||
bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y;
|
||||
sourceBottomRightX = sourceBottomRightY = dimMinusThree;
|
||||
}
|
||||
|
||||
return PerspectiveTransform.quadrilateralToQuadrilateral(
|
||||
3.5f,
|
||||
3.5f,
|
||||
dimMinusThree,
|
||||
3.5f,
|
||||
sourceBottomRightX,
|
||||
sourceBottomRightY,
|
||||
3.5f,
|
||||
dimMinusThree,
|
||||
topLeft.X,
|
||||
topLeft.Y,
|
||||
topRight.X,
|
||||
topRight.Y,
|
||||
bottomRightX,
|
||||
bottomRightY,
|
||||
bottomLeft.X,
|
||||
bottomLeft.Y);
|
||||
}
|
||||
|
||||
private static BitMatrix sampleGrid(BitMatrix image, PerspectiveTransform transform, int dimension)
|
||||
{
|
||||
GridSampler sampler = GridSampler.Instance;
|
||||
return sampler.sampleGrid(image, dimension, dimension, transform);
|
||||
}
|
||||
|
||||
/// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position
|
||||
/// of the finder patterns and estimated module size.</p>
|
||||
/// </summary>
|
||||
private static bool computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize, out int dimension)
|
||||
{
|
||||
int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize);
|
||||
int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize);
|
||||
dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
|
||||
switch (dimension & 0x03)
|
||||
{
|
||||
// mod 4
|
||||
case 0:
|
||||
dimension++;
|
||||
break;
|
||||
// 1? do nothing
|
||||
case 2:
|
||||
dimension--;
|
||||
break;
|
||||
case 3:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Computes an average estimated module size based on estimated derived from the positions
|
||||
/// of the three finder patterns.</p>
|
||||
/// </summary>
|
||||
/// <param name="topLeft">detected top-left finder pattern center</param>
|
||||
/// <param name="topRight">detected top-right finder pattern center</param>
|
||||
/// <param name="bottomLeft">detected bottom-left finder pattern center</param>
|
||||
/// <returns>estimated module size</returns>
|
||||
protected internal virtual float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft)
|
||||
{
|
||||
// Take the average
|
||||
return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
|
||||
}
|
||||
|
||||
/// <summary> <p>Estimates module size based on two finder patterns -- it uses
|
||||
/// {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
|
||||
/// width of each, measuring along the axis between their centers.</p>
|
||||
/// </summary>
|
||||
private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern.X, (int)pattern.Y, (int)otherPattern.X, (int)otherPattern.Y);
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern.X, (int)otherPattern.Y, (int)pattern.X, (int)pattern.Y);
|
||||
if (Single.IsNaN(moduleSizeEst1))
|
||||
{
|
||||
return moduleSizeEst2 / 7.0f;
|
||||
}
|
||||
if (Single.IsNaN(moduleSizeEst2))
|
||||
{
|
||||
return moduleSizeEst1 / 7.0f;
|
||||
}
|
||||
// Average them, and divide by 7 since we've counted the width of 3 black modules,
|
||||
// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
|
||||
return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
|
||||
}
|
||||
|
||||
/// <summary> See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
|
||||
/// a finder pattern by looking for a black-white-black run from the center in the direction
|
||||
/// of another point (another finder pattern center), and in the opposite direction too.
|
||||
/// </summary>
|
||||
private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY)
|
||||
{
|
||||
|
||||
float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
|
||||
|
||||
// Now count other way -- don't run off image though of course
|
||||
float scale = 1.0f;
|
||||
int otherToX = fromX - (toX - fromX);
|
||||
if (otherToX < 0)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
scale = (float)fromX / (float)(fromX - otherToX);
|
||||
otherToX = 0;
|
||||
}
|
||||
else if (otherToX >= image.Width)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
scale = (float)(image.Width - 1 - fromX) / (float)(otherToX - fromX);
|
||||
otherToX = image.Width - 1;
|
||||
}
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
int otherToY = (int)(fromY - (toY - fromY) * scale);
|
||||
|
||||
scale = 1.0f;
|
||||
if (otherToY < 0)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
scale = (float)fromY / (float)(fromY - otherToY);
|
||||
otherToY = 0;
|
||||
}
|
||||
else if (otherToY >= image.Height)
|
||||
{
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
scale = (float)(image.Height - 1 - fromY) / (float)(otherToY - fromY);
|
||||
otherToY = image.Height - 1;
|
||||
}
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
otherToX = (int)(fromX + (otherToX - fromX) * scale);
|
||||
|
||||
result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
|
||||
return result - 1.0f; // -1 because we counted the middle pixel twice
|
||||
}
|
||||
|
||||
/// <summary> <p>This method traces a line from a point in the image, in the direction towards another point.
|
||||
/// It begins in a black region, and keeps going until it finds white, then black, then white again.
|
||||
/// It reports the distance from the start to this point.</p>
|
||||
///
|
||||
/// <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
|
||||
/// may be skewed or rotated.</p>
|
||||
/// </summary>
|
||||
private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY)
|
||||
{
|
||||
// Mild variant of Bresenham's algorithm;
|
||||
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
bool steep = Math.Abs(toY - fromY) > Math.Abs(toX - fromX);
|
||||
if (steep)
|
||||
{
|
||||
int temp = fromX;
|
||||
fromX = fromY;
|
||||
fromY = temp;
|
||||
temp = toX;
|
||||
toX = toY;
|
||||
toY = temp;
|
||||
}
|
||||
|
||||
int dx = Math.Abs(toX - fromX);
|
||||
int dy = Math.Abs(toY - fromY);
|
||||
int error = -dx >> 1;
|
||||
int xstep = fromX < toX ? 1 : -1;
|
||||
int ystep = fromY < toY ? 1 : -1;
|
||||
|
||||
// In black pixels, looking for white, first or second time.
|
||||
int state = 0;
|
||||
// Loop up until x == toX, but not beyond
|
||||
int xLimit = toX + xstep;
|
||||
for (int x = fromX, y = fromY; x != xLimit; x += xstep)
|
||||
{
|
||||
int realX = steep ? y : x;
|
||||
int realY = steep ? x : y;
|
||||
|
||||
// Does current pixel mean we have moved white to black or vice versa?
|
||||
// Scanning black in state 0,2 and white in state 1, so if we find the wrong
|
||||
// color, advance to next state or end if we are in state 2 already
|
||||
if ((state == 1) == image[realX, realY])
|
||||
{
|
||||
if (state == 2)
|
||||
{
|
||||
return MathUtils.distance(x, y, fromX, fromY);
|
||||
}
|
||||
state++;
|
||||
}
|
||||
error += dy;
|
||||
if (error > 0)
|
||||
{
|
||||
if (y == toY)
|
||||
{
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
y += ystep;
|
||||
error -= dx;
|
||||
}
|
||||
}
|
||||
// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
|
||||
// is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a
|
||||
// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
|
||||
if (state == 2)
|
||||
{
|
||||
return MathUtils.distance(toX + xstep, toY, fromX, fromY);
|
||||
}
|
||||
// else we didn't find even black-white-black; no estimate is really possible
|
||||
return Single.NaN;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>Attempts to locate an alignment pattern in a limited region of the image, which is
|
||||
/// guessed to contain it. This method uses {@link AlignmentPattern}.</p>
|
||||
/// </summary>
|
||||
/// <param name="overallEstModuleSize">estimated module size so far</param>
|
||||
/// <param name="estAlignmentX">x coordinate of center of area probably containing alignment pattern</param>
|
||||
/// <param name="estAlignmentY">y coordinate of above</param>
|
||||
/// <param name="allowanceFactor">number of pixels in all directions to search from the center</param>
|
||||
/// <returns>
|
||||
/// <see cref="AlignmentPattern"/> if found, or null otherwise
|
||||
/// </returns>
|
||||
protected AlignmentPattern findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, float allowanceFactor)
|
||||
{
|
||||
// Look for an alignment pattern (3 modules in size) around where it
|
||||
// should be
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
int allowance = (int)(allowanceFactor * overallEstModuleSize);
|
||||
int alignmentAreaLeftX = Math.Max(0, estAlignmentX - allowance);
|
||||
int alignmentAreaRightX = Math.Min(image.Width - 1, estAlignmentX + allowance);
|
||||
if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int alignmentAreaTopY = Math.Max(0, estAlignmentY - allowance);
|
||||
int alignmentAreaBottomY = Math.Min(image.Height - 1, estAlignmentY + allowance);
|
||||
|
||||
var alignmentFinder = new AlignmentPatternFinder(
|
||||
image,
|
||||
alignmentAreaLeftX,
|
||||
alignmentAreaTopY,
|
||||
alignmentAreaRightX - alignmentAreaLeftX,
|
||||
alignmentAreaBottomY - alignmentAreaTopY,
|
||||
overallEstModuleSize,
|
||||
resultPointCallback);
|
||||
|
||||
return alignmentFinder.find();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Others/zxing.core/zxing.core/qrcode/detector/FinderPattern.cs
Normal file
107
Others/zxing.core/zxing.core/qrcode/detector/FinderPattern.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>Encapsulates a finder pattern, which are the three square patterns found in
|
||||
/// the corners of QR Codes. It also encapsulates a count of similar finder patterns,
|
||||
/// as a convenience to the finder's bookkeeping.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public sealed class FinderPattern : ResultPoint
|
||||
{
|
||||
private readonly float estimatedModuleSize;
|
||||
private int count;
|
||||
|
||||
internal FinderPattern(float posX, float posY, float estimatedModuleSize)
|
||||
: this(posX, posY, estimatedModuleSize, 1)
|
||||
{
|
||||
this.estimatedModuleSize = estimatedModuleSize;
|
||||
this.count = 1;
|
||||
}
|
||||
|
||||
internal FinderPattern(float posX, float posY, float estimatedModuleSize, int count)
|
||||
: base(posX, posY)
|
||||
{
|
||||
this.estimatedModuleSize = estimatedModuleSize;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the estimated module.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The size of the estimated module.
|
||||
/// </value>
|
||||
public float EstimatedModuleSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return estimatedModuleSize;
|
||||
}
|
||||
}
|
||||
|
||||
internal int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
internal void incrementCount()
|
||||
{
|
||||
this.count++;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary> <p>Determines if this finder pattern "about equals" a finder pattern at the stated
|
||||
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
/// </summary>
|
||||
internal bool aboutEquals(float moduleSize, float i, float j)
|
||||
{
|
||||
if (Math.Abs(i - Y) <= moduleSize && Math.Abs(j - X) <= moduleSize)
|
||||
{
|
||||
float moduleSizeDiff = Math.Abs(moduleSize - estimatedModuleSize);
|
||||
return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines this object's current estimate of a finder pattern position and module size
|
||||
/// with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
|
||||
/// based on count.
|
||||
/// </summary>
|
||||
/// <param name="i">The i.</param>
|
||||
/// <param name="j">The j.</param>
|
||||
/// <param name="newModuleSize">New size of the module.</param>
|
||||
/// <returns></returns>
|
||||
internal FinderPattern combineEstimate(float i, float j, float newModuleSize)
|
||||
{
|
||||
int combinedCount = count + 1;
|
||||
float combinedX = (count * X + j) / combinedCount;
|
||||
float combinedY = (count * Y + i) / combinedCount;
|
||||
float combinedModuleSize = (count * estimatedModuleSize + newModuleSize) / combinedCount;
|
||||
return new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
* Copyright 2007 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
||||
/// markers at three corners of a QR Code.</p>
|
||||
///
|
||||
/// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public class FinderPatternFinder
|
||||
{
|
||||
private const int CENTER_QUORUM = 2;
|
||||
private static readonly EstimatedModuleComparator moduleComparator = new EstimatedModuleComparator();
|
||||
/// <summary>
|
||||
/// 1 pixel/module times 3 modules/center
|
||||
/// </summary>
|
||||
protected internal const int MIN_SKIP = 3;
|
||||
|
||||
/// <summary>
|
||||
/// support up to version 20 for mobile clients
|
||||
/// </summary>
|
||||
protected internal const int MAX_MODULES = 97;
|
||||
|
||||
private const int INTEGER_MATH_SHIFT = 8;
|
||||
|
||||
private readonly BitMatrix image;
|
||||
private readonly List<FinderPattern> possibleCenters;
|
||||
private bool hasSkipped;
|
||||
private readonly int[] crossCheckStateCount;
|
||||
private readonly ResultPointCallback resultPointCallback;
|
||||
|
||||
/// <summary>
|
||||
/// <p>Creates a finder that will search the image for three finder patterns.</p>
|
||||
/// </summary>
|
||||
/// <param name="image">image to search</param>
|
||||
public FinderPatternFinder(BitMatrix image)
|
||||
: this(image, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FinderPatternFinder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="image">The image.</param>
|
||||
/// <param name="resultPointCallback">The result point callback.</param>
|
||||
public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback)
|
||||
{
|
||||
this.image = image;
|
||||
this.possibleCenters = new List<FinderPattern>();
|
||||
this.crossCheckStateCount = new int[5];
|
||||
this.resultPointCallback = resultPointCallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image.
|
||||
/// </summary>
|
||||
protected internal virtual BitMatrix Image
|
||||
{
|
||||
get { return image; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the possible centers.
|
||||
/// </summary>
|
||||
protected internal virtual List<FinderPattern> PossibleCenters
|
||||
{
|
||||
get { return possibleCenters; }
|
||||
}
|
||||
|
||||
internal virtual FinderPatternInfo find(IDictionary<DecodeHintType, object> hints)
|
||||
{
|
||||
bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);
|
||||
int maxI = image.Height;
|
||||
int maxJ = image.Width;
|
||||
// We are looking for black/white/black/white/black modules in
|
||||
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
||||
|
||||
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||
// QR versions regardless of how dense they are.
|
||||
int iSkip = (3 * maxI) / (4 * MAX_MODULES);
|
||||
if (iSkip < MIN_SKIP || tryHarder)
|
||||
{
|
||||
iSkip = MIN_SKIP;
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
int[] stateCount = new int[5];
|
||||
for (int i = iSkip - 1; i < maxI && !done; i += iSkip)
|
||||
{
|
||||
// Get a row of black/white values
|
||||
doClearCounts(stateCount);
|
||||
int currentState = 0;
|
||||
for (int j = 0; j < maxJ; j++)
|
||||
{
|
||||
if (image[j, i])
|
||||
{
|
||||
// Black pixel
|
||||
if ((currentState & 1) == 1)
|
||||
{
|
||||
// Counting white pixels
|
||||
currentState++;
|
||||
}
|
||||
stateCount[currentState]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// White pixel
|
||||
if ((currentState & 1) == 0)
|
||||
{
|
||||
// Counting black pixels
|
||||
if (currentState == 4)
|
||||
{
|
||||
// A winner?
|
||||
if (foundPatternCross(stateCount))
|
||||
{
|
||||
// Yes
|
||||
bool confirmed = handlePossibleCenter(stateCount, i, j);
|
||||
if (confirmed)
|
||||
{
|
||||
// Start examining every other line. Checking each line turned out to be too
|
||||
// expensive and didn't improve performance.
|
||||
iSkip = 2;
|
||||
if (hasSkipped)
|
||||
{
|
||||
done = haveMultiplyConfirmedCenters();
|
||||
}
|
||||
else
|
||||
{
|
||||
int rowSkip = findRowSkip();
|
||||
if (rowSkip > stateCount[2])
|
||||
{
|
||||
// Skip rows between row of lower confirmed center
|
||||
// and top of presumed third confirmed center
|
||||
// but back up a bit to get a full chance of detecting
|
||||
// it, entire width of center of finder pattern
|
||||
|
||||
// Skip by rowSkip, but back off by stateCount[2] (size of last center
|
||||
// of pattern we saw) to be conservative, and also back off by iSkip which
|
||||
// is about to be re-added
|
||||
i += rowSkip - stateCount[2] - iSkip;
|
||||
j = maxJ - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
doShiftCounts2(stateCount);
|
||||
currentState = 3;
|
||||
continue;
|
||||
}
|
||||
// Clear state to start looking again
|
||||
currentState = 0;
|
||||
doClearCounts(stateCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, shift counts back by two
|
||||
doShiftCounts2(stateCount);
|
||||
currentState = 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stateCount[++currentState]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counting white pixels
|
||||
stateCount[currentState]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundPatternCross(stateCount))
|
||||
{
|
||||
bool confirmed = handlePossibleCenter(stateCount, i, maxJ);
|
||||
if (confirmed)
|
||||
{
|
||||
iSkip = stateCount[0];
|
||||
if (hasSkipped)
|
||||
{
|
||||
// Found a third one
|
||||
done = haveMultiplyConfirmedCenters();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinderPattern[] patternInfo = selectBestPatterns();
|
||||
if (patternInfo == null)
|
||||
return null;
|
||||
|
||||
ResultPoint.orderBestPatterns(patternInfo);
|
||||
|
||||
return new FinderPatternInfo(patternInfo);
|
||||
}
|
||||
|
||||
/// <summary> Given a count of black/white/black/white/black pixels just seen and an end position,
|
||||
/// figures the location of the center of this run.
|
||||
/// </summary>
|
||||
private static float? centerFromEnd(int[] stateCount, int end)
|
||||
{
|
||||
var result = (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
|
||||
if (Single.IsNaN(result))
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <param name="stateCount">count of black/white/black/white/black pixels just read
|
||||
/// </param>
|
||||
/// <returns> true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
|
||||
/// used by finder patterns to be considered a match
|
||||
/// </returns>
|
||||
protected internal static bool foundPatternCross(int[] stateCount)
|
||||
{
|
||||
int totalModuleSize = 0;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = stateCount[i];
|
||||
if (count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
totalModuleSize += count;
|
||||
}
|
||||
if (totalModuleSize < 7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7;
|
||||
int maxVariance = moduleSize / 2;
|
||||
// Allow less than 50% variance from 1-1-3-1-1 proportions
|
||||
return Math.Abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance &&
|
||||
Math.Abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance &&
|
||||
Math.Abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance &&
|
||||
Math.Abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance &&
|
||||
Math.Abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="stateCount">count of black/white/black/white/black pixels just read</param>
|
||||
/// <returns>true if the proportions of the counts is close enough to the 1/1/3/1/1 ratios
|
||||
/// by finder patterns to be considered a match</returns>
|
||||
protected static bool foundPatternDiagonal(int[] stateCount)
|
||||
{
|
||||
int totalModuleSize = 0;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = stateCount[i];
|
||||
if (count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
totalModuleSize += count;
|
||||
}
|
||||
if (totalModuleSize < 7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
float moduleSize = totalModuleSize / 7.0f;
|
||||
float maxVariance = moduleSize / 1.333f;
|
||||
// Allow less than 75% variance from 1-1-3-1-1 proportions
|
||||
return
|
||||
Math.Abs(moduleSize - stateCount[0]) < maxVariance &&
|
||||
Math.Abs(moduleSize - stateCount[1]) < maxVariance &&
|
||||
Math.Abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance &&
|
||||
Math.Abs(moduleSize - stateCount[3]) < maxVariance &&
|
||||
Math.Abs(moduleSize - stateCount[4]) < maxVariance;
|
||||
}
|
||||
|
||||
private int[] CrossCheckStateCount
|
||||
{
|
||||
get
|
||||
{
|
||||
doClearCounts(crossCheckStateCount);
|
||||
return crossCheckStateCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets everything to 0
|
||||
/// </summary>
|
||||
/// <param name="counts"></param>
|
||||
[Obsolete]
|
||||
protected void clearCounts(int[] counts)
|
||||
{
|
||||
doClearCounts(counts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// shifts left by 2 index
|
||||
/// </summary>
|
||||
/// <param name="stateCount"></param>
|
||||
[Obsolete]
|
||||
protected void shiftCounts2(int[] stateCount)
|
||||
{
|
||||
doShiftCounts2(stateCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets everything to 0
|
||||
/// </summary>
|
||||
/// <param name="counts"></param>
|
||||
protected static void doClearCounts(int[] counts)
|
||||
{
|
||||
SupportClass.Fill(counts, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// shifts left by 2 index
|
||||
/// </summary>
|
||||
/// <param name="stateCount"></param>
|
||||
protected static void doShiftCounts2(int[] stateCount)
|
||||
{
|
||||
stateCount[0] = stateCount[2];
|
||||
stateCount[1] = stateCount[3];
|
||||
stateCount[2] = stateCount[4];
|
||||
stateCount[3] = 1;
|
||||
stateCount[4] = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a vertical and horizontal scan finds a potential finder pattern, this method
|
||||
/// "cross-cross-cross-checks" by scanning down diagonally through the center of the possible
|
||||
/// finder pattern to see if the same proportion is detected.
|
||||
/// </summary>
|
||||
/// <param name="centerI">row where a finder pattern was detected</param>
|
||||
/// <param name="centerJ">center of the section that appears to cross a finder pattern</param>
|
||||
/// <returns>true if proportions are withing expected limits</returns>
|
||||
private bool crossCheckDiagonal(int centerI, int centerJ)
|
||||
{
|
||||
int[] stateCount = CrossCheckStateCount;
|
||||
|
||||
// Start counting up, left from center finding black center mass
|
||||
int i = 0;
|
||||
while (centerI >= i && centerJ >= i && image[centerJ - i, centerI - i])
|
||||
{
|
||||
stateCount[2]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[2] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue up, left finding white space
|
||||
while (centerI >= i && centerJ >= i && !image[centerJ - i, centerI - i])
|
||||
{
|
||||
stateCount[1]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[1] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue up, left finding black border
|
||||
while (centerI >= i && centerJ >= i && image[centerJ - i, centerI - i])
|
||||
{
|
||||
stateCount[0]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[0] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxI = image.Height;
|
||||
int maxJ = image.Width;
|
||||
|
||||
// Now also count down, right from center
|
||||
i = 1;
|
||||
while (centerI + i < maxI && centerJ + i < maxJ && image[centerJ + i, centerI + i])
|
||||
{
|
||||
stateCount[2]++;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (centerI + i < maxI && centerJ + i < maxJ && !image[centerJ + i, centerI + i])
|
||||
{
|
||||
stateCount[3]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[3] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (centerI + i < maxI && centerJ + i < maxJ && image[centerJ + i, centerI + i])
|
||||
{
|
||||
stateCount[4]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[4] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return foundPatternDiagonal(stateCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>After a horizontal scan finds a potential finder pattern, this method
|
||||
/// "cross-checks" by scanning down vertically through the center of the possible
|
||||
/// finder pattern to see if the same proportion is detected.</p>
|
||||
/// </summary>
|
||||
/// <param name="startI">row where a finder pattern was detected</param>
|
||||
/// <param name="centerJ">center of the section that appears to cross a finder pattern</param>
|
||||
/// <param name="maxCount">maximum reasonable number of modules that should be
|
||||
/// observed in any reading state, based on the results of the horizontal scan</param>
|
||||
/// <param name="originalStateCountTotal">The original state count total.</param>
|
||||
/// <returns>
|
||||
/// vertical center of finder pattern, or null if not found
|
||||
/// </returns>
|
||||
private float? crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal)
|
||||
{
|
||||
int maxI = image.Height;
|
||||
int[] stateCount = CrossCheckStateCount;
|
||||
|
||||
// Start counting up from center
|
||||
int i = startI;
|
||||
while (i >= 0 && image[centerJ, i])
|
||||
{
|
||||
stateCount[2]++;
|
||||
i--;
|
||||
}
|
||||
if (i < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i >= 0 && !image[centerJ, i] && stateCount[1] <= maxCount)
|
||||
{
|
||||
stateCount[1]++;
|
||||
i--;
|
||||
}
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if (i < 0 || stateCount[1] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i >= 0 && image[centerJ, i] && stateCount[0] <= maxCount)
|
||||
{
|
||||
stateCount[0]++;
|
||||
i--;
|
||||
}
|
||||
if (stateCount[0] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
i = startI + 1;
|
||||
while (i < maxI && image[centerJ, i])
|
||||
{
|
||||
stateCount[2]++;
|
||||
i++;
|
||||
}
|
||||
if (i == maxI)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i < maxI && !image[centerJ, i] && stateCount[3] < maxCount)
|
||||
{
|
||||
stateCount[3]++;
|
||||
i++;
|
||||
}
|
||||
if (i == maxI || stateCount[3] >= maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (i < maxI && image[centerJ, i] && stateCount[4] < maxCount)
|
||||
{
|
||||
stateCount[4]++;
|
||||
i++;
|
||||
}
|
||||
if (stateCount[4] >= maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we found a finder-pattern-like section, but its size is more than 40% different than
|
||||
// the original, assume it's a false positive
|
||||
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
|
||||
if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : null;
|
||||
}
|
||||
|
||||
/// <summary> <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
|
||||
/// except it reads horizontally instead of vertically. This is used to cross-cross
|
||||
/// check a vertical cross check and locate the real center of the alignment pattern.</p>
|
||||
/// </summary>
|
||||
private float? crossCheckHorizontal(int startJ, int centerI, int maxCount, int originalStateCountTotal)
|
||||
{
|
||||
int maxJ = image.Width;
|
||||
int[] stateCount = CrossCheckStateCount;
|
||||
|
||||
int j = startJ;
|
||||
while (j >= 0 && image[j, centerI])
|
||||
{
|
||||
stateCount[2]++;
|
||||
j--;
|
||||
}
|
||||
if (j < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (j >= 0 && !image[j, centerI] && stateCount[1] <= maxCount)
|
||||
{
|
||||
stateCount[1]++;
|
||||
j--;
|
||||
}
|
||||
if (j < 0 || stateCount[1] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (j >= 0 && image[j, centerI] && stateCount[0] <= maxCount)
|
||||
{
|
||||
stateCount[0]++;
|
||||
j--;
|
||||
}
|
||||
if (stateCount[0] > maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
j = startJ + 1;
|
||||
while (j < maxJ && image[j, centerI])
|
||||
{
|
||||
stateCount[2]++;
|
||||
j++;
|
||||
}
|
||||
if (j == maxJ)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (j < maxJ && !image[j, centerI] && stateCount[3] < maxCount)
|
||||
{
|
||||
stateCount[3]++;
|
||||
j++;
|
||||
}
|
||||
if (j == maxJ || stateCount[3] >= maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
while (j < maxJ && image[j, centerI] && stateCount[4] < maxCount)
|
||||
{
|
||||
stateCount[4]++;
|
||||
j++;
|
||||
}
|
||||
if (stateCount[4] >= maxCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we found a finder-pattern-like section, but its size is significantly different than
|
||||
// the original, assume it's a false positive
|
||||
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
|
||||
if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// @see #handlePossibleCenter(int[], int, int)
|
||||
/// </summary>
|
||||
/// <param name="stateCount">reading state module counts from horizontal scan</param>
|
||||
/// <param name="i">row where finder pattern may be found</param>
|
||||
/// <param name="j">end of possible finder pattern in row</param>
|
||||
/// <param name="pureBarcode">ignored</param>
|
||||
/// <returns>true if a finder pattern candidate was found this time</returns>
|
||||
[Obsolete("only exists for backwards compatibility")]
|
||||
protected bool handlePossibleCenter(int[] stateCount, int i, int j, bool pureBarcode)
|
||||
{
|
||||
return handlePossibleCenter(stateCount, i, j);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <p>This is called when a horizontal scan finds a possible alignment pattern. It will
|
||||
/// cross check with a vertical scan, and if successful, will, ah, cross-cross-check
|
||||
/// with another horizontal scan. This is needed primarily to locate the real horizontal
|
||||
/// center of the pattern in cases of extreme skew.
|
||||
/// And then we cross-cross-cross check with another diagonal scan.</p>
|
||||
/// If that succeeds the finder pattern location is added to a list that tracks
|
||||
/// the number of times each location has been nearly-matched as a finder pattern.
|
||||
/// Each additional find is more evidence that the location is in fact a finder
|
||||
/// pattern center
|
||||
/// </summary>
|
||||
/// <param name="stateCount">reading state module counts from horizontal scan</param>
|
||||
/// <param name="i">row where finder pattern may be found</param>
|
||||
/// <param name="j">end of possible finder pattern in row</param>
|
||||
/// <returns>
|
||||
/// true if a finder pattern candidate was found this time
|
||||
/// </returns>
|
||||
protected bool handlePossibleCenter(int[] stateCount, int i, int j)
|
||||
{
|
||||
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
|
||||
stateCount[4];
|
||||
float? centerJ = centerFromEnd(stateCount, j);
|
||||
if (centerJ == null)
|
||||
return false;
|
||||
float? centerI = crossCheckVertical(i, (int) centerJ.Value, stateCount[2], stateCountTotal);
|
||||
if (centerI != null)
|
||||
{
|
||||
// Re-cross check
|
||||
centerJ = crossCheckHorizontal((int) centerJ.Value, (int) centerI.Value, stateCount[2], stateCountTotal);
|
||||
if (centerJ != null && crossCheckDiagonal((int) centerI, (int) centerJ))
|
||||
{
|
||||
float estimatedModuleSize = stateCountTotal / 7.0f;
|
||||
bool found = false;
|
||||
for (int index = 0; index < possibleCenters.Count; index++)
|
||||
{
|
||||
var center = possibleCenters[index];
|
||||
// Look for about the same center and module size:
|
||||
if (center.aboutEquals(estimatedModuleSize, centerI.Value, centerJ.Value))
|
||||
{
|
||||
possibleCenters.RemoveAt(index);
|
||||
possibleCenters.Insert(index, center.combineEstimate(centerI.Value, centerJ.Value, estimatedModuleSize));
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
var point = new FinderPattern(centerJ.Value, centerI.Value, estimatedModuleSize);
|
||||
|
||||
possibleCenters.Add(point);
|
||||
if (resultPointCallback != null)
|
||||
{
|
||||
|
||||
resultPointCallback(point);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <returns> number of rows we could safely skip during scanning, based on the first
|
||||
/// two finder patterns that have been located. In some cases their position will
|
||||
/// allow us to infer that the third pattern must lie below a certain point farther
|
||||
/// down in the image.
|
||||
/// </returns>
|
||||
private int findRowSkip()
|
||||
{
|
||||
int max = possibleCenters.Count;
|
||||
if (max <= 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ResultPoint firstConfirmedCenter = null;
|
||||
foreach (var center in possibleCenters)
|
||||
{
|
||||
if (center.Count >= CENTER_QUORUM)
|
||||
{
|
||||
if (firstConfirmedCenter == null)
|
||||
{
|
||||
firstConfirmedCenter = center;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have two confirmed centers
|
||||
// How far down can we skip before resuming looking for the next
|
||||
// pattern? In the worst case, only the difference between the
|
||||
// difference in the x / y coordinates of the two centers.
|
||||
// This is the case where you find top left last.
|
||||
hasSkipped = true;
|
||||
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
|
||||
return (int) (Math.Abs(firstConfirmedCenter.X - center.X) - Math.Abs(firstConfirmedCenter.Y - center.Y)) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <returns> true iff we have found at least 3 finder patterns that have been detected
|
||||
/// at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
|
||||
/// candidates is "pretty similar"
|
||||
/// </returns>
|
||||
private bool haveMultiplyConfirmedCenters()
|
||||
{
|
||||
int confirmedCount = 0;
|
||||
float totalModuleSize = 0.0f;
|
||||
int max = possibleCenters.Count;
|
||||
foreach (var pattern in possibleCenters)
|
||||
{
|
||||
if (pattern.Count >= CENTER_QUORUM)
|
||||
{
|
||||
confirmedCount++;
|
||||
totalModuleSize += pattern.EstimatedModuleSize;
|
||||
}
|
||||
}
|
||||
if (confirmedCount < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
|
||||
// and that we need to keep looking. We detect this by asking if the estimated module sizes
|
||||
// vary too much. We arbitrarily say that when the total deviation from average exceeds
|
||||
// 5% of the total module size estimates, it's too much.
|
||||
float average = totalModuleSize / max;
|
||||
float totalDeviation = 0.0f;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var pattern = possibleCenters[i];
|
||||
totalDeviation += Math.Abs(pattern.EstimatedModuleSize - average);
|
||||
}
|
||||
return totalDeviation <= 0.05f * totalModuleSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get square of distance between a and b.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
private static double squaredDistance(FinderPattern a, FinderPattern b)
|
||||
{
|
||||
double x = a.X - b.X;
|
||||
double y = a.Y - b.Y;
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/// <returns> the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
||||
/// those have similar module size and form a shape closer to a isosceles right triangle.
|
||||
/// </returns>
|
||||
private FinderPattern[] selectBestPatterns()
|
||||
{
|
||||
int startSize = possibleCenters.Count;
|
||||
if (startSize < 3)
|
||||
{
|
||||
// Couldn't find enough finder patterns
|
||||
return null;
|
||||
}
|
||||
|
||||
possibleCenters.Sort(moduleComparator);
|
||||
|
||||
double distortion = Double.MaxValue;
|
||||
FinderPattern[] bestPatterns = new FinderPattern[3];
|
||||
|
||||
for (int i = 0; i < possibleCenters.Count - 2; i++)
|
||||
{
|
||||
FinderPattern fpi = possibleCenters[i];
|
||||
float minModuleSize = fpi.EstimatedModuleSize;
|
||||
|
||||
for (int j = i + 1; j < possibleCenters.Count - 1; j++)
|
||||
{
|
||||
FinderPattern fpj = possibleCenters[j];
|
||||
double squares0 = squaredDistance(fpi, fpj);
|
||||
|
||||
for (int k = j + 1; k < possibleCenters.Count; k++)
|
||||
{
|
||||
FinderPattern fpk = possibleCenters[k];
|
||||
float maxModuleSize = fpk.EstimatedModuleSize;
|
||||
if (maxModuleSize > minModuleSize * 1.4f)
|
||||
{
|
||||
// module size is not similar
|
||||
continue;
|
||||
}
|
||||
|
||||
var a = squares0;
|
||||
var b = squaredDistance(fpj, fpk);
|
||||
var c = squaredDistance(fpi, fpk);
|
||||
|
||||
// sorts ascending - inlined
|
||||
if (a < b)
|
||||
{
|
||||
if (b > c)
|
||||
{
|
||||
if (a < c)
|
||||
{
|
||||
var temp = b;
|
||||
b = c;
|
||||
c = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
var temp = a;
|
||||
a = c;
|
||||
c = b;
|
||||
b = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b < c)
|
||||
{
|
||||
if (a < c)
|
||||
{
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = c;
|
||||
c = temp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var temp = a;
|
||||
a = c;
|
||||
c = temp;
|
||||
}
|
||||
}
|
||||
|
||||
// a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle).
|
||||
// Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0,
|
||||
// we need to check both two equal sides separately.
|
||||
// The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity
|
||||
// from isosceles right triangle.
|
||||
double d = Math.Abs(c - 2 * b) + Math.Abs(c - 2 * a);
|
||||
if (d < distortion)
|
||||
{
|
||||
distortion = d;
|
||||
bestPatterns[0] = fpi;
|
||||
bestPatterns[1] = fpj;
|
||||
bestPatterns[2] = fpk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (distortion == Double.MaxValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return bestPatterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders by {@link FinderPatternFinder#getEstimatedModuleSize()}
|
||||
/// </summary>
|
||||
private sealed class EstimatedModuleComparator : IComparer<FinderPattern>
|
||||
{
|
||||
public int Compare(FinderPattern center1, FinderPattern center2)
|
||||
{
|
||||
if (center1.EstimatedModuleSize == center2.EstimatedModuleSize)
|
||||
return 0;
|
||||
if (center1.EstimatedModuleSize < center2.EstimatedModuleSize)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2007 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.
|
||||
*/
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>Encapsulates information about finder patterns in an image, including the location of
|
||||
/// the three finder patterns, and their estimated module size.</p>
|
||||
/// </summary>
|
||||
/// <author>Sean Owen</author>
|
||||
public sealed class FinderPatternInfo
|
||||
{
|
||||
private readonly FinderPattern bottomLeft;
|
||||
private readonly FinderPattern topLeft;
|
||||
private readonly FinderPattern topRight;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FinderPatternInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patternCenters">The pattern centers.</param>
|
||||
public FinderPatternInfo(FinderPattern[] patternCenters)
|
||||
{
|
||||
this.bottomLeft = patternCenters[0];
|
||||
this.topLeft = patternCenters[1];
|
||||
this.topRight = patternCenters[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bottom left.
|
||||
/// </summary>
|
||||
public FinderPattern BottomLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
return bottomLeft;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top left.
|
||||
/// </summary>
|
||||
public FinderPattern TopLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
return topLeft;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top right.
|
||||
/// </summary>
|
||||
public FinderPattern TopRight
|
||||
{
|
||||
get
|
||||
{
|
||||
return topRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Others/zxing.core/zxing.core/qrcode/encoder/BlockPair.cs
Normal file
40
Others/zxing.core/zxing.core/qrcode/encoder/BlockPair.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2008 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.
|
||||
*/
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
internal sealed class BlockPair
|
||||
{
|
||||
private readonly byte[] dataBytes;
|
||||
private readonly byte[] errorCorrectionBytes;
|
||||
|
||||
public BlockPair(byte[] data, byte[] errorCorrection)
|
||||
{
|
||||
dataBytes = data;
|
||||
errorCorrectionBytes = errorCorrection;
|
||||
}
|
||||
|
||||
public byte[] DataBytes
|
||||
{
|
||||
get { return dataBytes; }
|
||||
}
|
||||
|
||||
public byte[] ErrorCorrectionBytes
|
||||
{
|
||||
get { return errorCorrectionBytes; }
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Others/zxing.core/zxing.core/qrcode/encoder/ByteMatrix.cs
Normal file
150
Others/zxing.core/zxing.core/qrcode/encoder/ByteMatrix.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2008 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.Text;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
|
||||
/// 0, 1 and 2 I'm going to use less memory and go with bytes.
|
||||
/// </summary>
|
||||
/// <author>dswitkin@google.com (Daniel Switkin)</author>
|
||||
public sealed class ByteMatrix
|
||||
{
|
||||
private readonly byte[][] bytes;
|
||||
private readonly int width;
|
||||
private readonly int height;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByteMatrix"/> class.
|
||||
/// </summary>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public ByteMatrix(int width, int height)
|
||||
{
|
||||
bytes = new byte[height][];
|
||||
for (var i = 0; i < height; i++)
|
||||
bytes[i] = new byte[width];
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height.
|
||||
/// </summary>
|
||||
public int Height
|
||||
{
|
||||
get { return height; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width.
|
||||
/// </summary>
|
||||
public int Width
|
||||
{
|
||||
get { return width; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="System.Int32"/> with the specified x.
|
||||
/// </summary>
|
||||
public int this[int x, int y]
|
||||
{
|
||||
get { return bytes[y][x]; }
|
||||
set { bytes[y][x] = (byte)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// an internal representation as bytes, in row-major order. array[y][x] represents point (x,y)
|
||||
/// </summary>
|
||||
public byte[][] Array
|
||||
{
|
||||
get { return bytes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void set(int x, int y, byte value)
|
||||
{
|
||||
bytes[y][x] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <param name="value">if set to <c>true</c> [value].</param>
|
||||
public void set(int x, int y, bool value)
|
||||
{
|
||||
bytes[y][x] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the specified value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public void clear(byte value)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
var bytesY = bytes[y];
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
bytesY[x] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String"/> that represents this instance.
|
||||
/// </returns>
|
||||
override public String ToString()
|
||||
{
|
||||
var result = new StringBuilder(2 * width * height + 2);
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
var bytesY = bytes[y];
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
switch (bytesY[x])
|
||||
{
|
||||
case 0:
|
||||
result.Append(" 0");
|
||||
break;
|
||||
case 1:
|
||||
result.Append(" 1");
|
||||
break;
|
||||
default:
|
||||
result.Append(" ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.Append('\n');
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
813
Others/zxing.core/zxing.core/qrcode/encoder/Encoder.cs
Normal file
813
Others/zxing.core/zxing.core/qrcode/encoder/Encoder.cs
Normal file
@@ -0,0 +1,813 @@
|
||||
/*
|
||||
* Copyright 2008 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;
|
||||
using ZXing.Common.ReedSolomon;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <author>satorux@google.com (Satoru Takabayashi) - creator</author>
|
||||
/// <author>dswitkin@google.com (Daniel Switkin) - ported from C++</author>
|
||||
public static class Encoder
|
||||
{
|
||||
|
||||
// The original table is defined in the table 5 of JISX0510:2004 (p.19).
|
||||
private static readonly int[] ALPHANUMERIC_TABLE = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
|
||||
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
|
||||
};
|
||||
|
||||
internal static String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
|
||||
|
||||
// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
|
||||
// Basically it applies four rules and summate all penalties.
|
||||
private static int calculateMaskPenalty(ByteMatrix matrix)
|
||||
{
|
||||
return MaskUtil.applyMaskPenaltyRule1(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule2(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule3(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule4(matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
|
||||
/// internally by chooseMode(). On success, store the result in "qrCode".
|
||||
/// We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
|
||||
/// "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
|
||||
/// strong error correction for this purpose.
|
||||
/// Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
|
||||
/// with which clients can specify the encoding mode. For now, we don't need the functionality.
|
||||
/// </summary>
|
||||
/// <param name="content">text to encode</param>
|
||||
/// <param name="ecLevel">error correction level to use</param>
|
||||
/// <returns><see cref="QRCode"/> representing the encoded QR code</returns>
|
||||
public static QRCode encode(String content, ErrorCorrectionLevel ecLevel)
|
||||
{
|
||||
return encode(content, ecLevel, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="ecLevel">The ec level.</param>
|
||||
/// <param name="hints">The hints.</param>
|
||||
/// <returns></returns>
|
||||
public static QRCode encode(String content,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
IDictionary<EncodeHintType, object> hints)
|
||||
{
|
||||
// Determine what character encoding has been specified by the caller, if any
|
||||
bool hasEncodingHint = hints != null && hints.ContainsKey(EncodeHintType.CHARACTER_SET);
|
||||
|
||||
#if !SILVERLIGHT || WINDOWS_PHONE
|
||||
var encoding = hints == null || !hints.ContainsKey(EncodeHintType.CHARACTER_SET) ? null : (String)hints[EncodeHintType.CHARACTER_SET];
|
||||
if (encoding == null)
|
||||
{
|
||||
encoding = DEFAULT_BYTE_MODE_ENCODING;
|
||||
}
|
||||
var generateECI = hasEncodingHint || !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding);
|
||||
#else
|
||||
// Silverlight supports only UTF-8 and UTF-16 out-of-the-box
|
||||
const string encoding = "UTF-8";
|
||||
// caller of the method can only control if the ECI segment should be written
|
||||
// character set is fixed to UTF-8; but some scanners doesn't like the ECI segment
|
||||
var generateECI = hasEncodingHint;
|
||||
#endif
|
||||
|
||||
// Pick an encoding mode appropriate for the content. Note that this will not attempt to use
|
||||
// multiple modes / segments even if that were more efficient. Twould be nice.
|
||||
var mode = chooseMode(content, encoding);
|
||||
|
||||
// This will store the header information, like mode and
|
||||
// length, as well as "header" segments like an ECI segment.
|
||||
var headerBits = new BitArray();
|
||||
|
||||
// Append ECI segment if applicable
|
||||
if (mode == Mode.BYTE && generateECI)
|
||||
{
|
||||
var eci = CharacterSetECI.getCharacterSetECIByName(encoding);
|
||||
if (eci != null)
|
||||
{
|
||||
var eciIsExplicitDisabled = (hints != null && hints.ContainsKey(EncodeHintType.DISABLE_ECI) && hints[EncodeHintType.DISABLE_ECI] != null && Convert.ToBoolean(hints[EncodeHintType.DISABLE_ECI].ToString()));
|
||||
if (!eciIsExplicitDisabled)
|
||||
{
|
||||
appendECI(eci, headerBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append the FNC1 mode header for GS1 formatted data if applicable
|
||||
var hasGS1FormatHint = hints != null && hints.ContainsKey(EncodeHintType.GS1_FORMAT);
|
||||
if (hasGS1FormatHint && hints[EncodeHintType.GS1_FORMAT] != null && Convert.ToBoolean(hints[EncodeHintType.GS1_FORMAT].ToString()))
|
||||
{
|
||||
// GS1 formatted codes are prefixed with a FNC1 in first position mode header
|
||||
appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits);
|
||||
}
|
||||
|
||||
// (With ECI in place,) Write the mode marker
|
||||
appendModeInfo(mode, headerBits);
|
||||
|
||||
// Collect data within the main segment, separately, to count its size if needed. Don't add it to
|
||||
// main payload yet.
|
||||
var dataBits = new BitArray();
|
||||
appendBytes(content, mode, dataBits, encoding);
|
||||
|
||||
Version version;
|
||||
if (hints != null && hints.ContainsKey(EncodeHintType.QR_VERSION))
|
||||
{
|
||||
int versionNumber = Int32.Parse(hints[EncodeHintType.QR_VERSION].ToString());
|
||||
version = Version.getVersionForNumber(versionNumber);
|
||||
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);
|
||||
if (!willFit(bitsNeeded, version, ecLevel))
|
||||
{
|
||||
throw new WriterException("Data too big for requested version");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
version = recommendVersion(ecLevel, mode, headerBits, dataBits);
|
||||
}
|
||||
|
||||
var headerAndDataBits = new BitArray();
|
||||
headerAndDataBits.appendBitArray(headerBits);
|
||||
// Find "length" of main segment and write it
|
||||
var numLetters = mode == Mode.BYTE ? dataBits.SizeInBytes : content.Length;
|
||||
appendLengthInfo(numLetters, version, mode, headerAndDataBits);
|
||||
// Put data together into the overall payload
|
||||
headerAndDataBits.appendBitArray(dataBits);
|
||||
|
||||
var ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
var numDataBytes = version.TotalCodewords - ecBlocks.TotalECCodewords;
|
||||
|
||||
// Terminate the bits properly.
|
||||
terminateBits(numDataBytes, headerAndDataBits);
|
||||
|
||||
// Interleave data bits with error correction code.
|
||||
var finalBits = interleaveWithECBytes(headerAndDataBits,
|
||||
version.TotalCodewords,
|
||||
numDataBytes,
|
||||
ecBlocks.NumBlocks);
|
||||
|
||||
var qrCode = new QRCode
|
||||
{
|
||||
ECLevel = ecLevel,
|
||||
Mode = mode,
|
||||
Version = version
|
||||
};
|
||||
|
||||
// Choose the mask pattern and set to "qrCode".
|
||||
var dimension = version.DimensionForVersion;
|
||||
var matrix = new ByteMatrix(dimension, dimension);
|
||||
|
||||
// Enable manual selection of the pattern to be used via hint
|
||||
var maskPattern = -1;
|
||||
if (hints != null && hints.ContainsKey(EncodeHintType.QR_MASK_PATTERN))
|
||||
{
|
||||
var hintMaskPattern = Int32.Parse(hints[EncodeHintType.QR_MASK_PATTERN].ToString());
|
||||
maskPattern = QRCode.isValidMaskPattern(hintMaskPattern) ? hintMaskPattern : -1;
|
||||
}
|
||||
|
||||
if (maskPattern == -1)
|
||||
{
|
||||
maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
|
||||
}
|
||||
qrCode.MaskPattern = maskPattern;
|
||||
|
||||
// Build the matrix and set it to "qrCode".
|
||||
MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
|
||||
qrCode.Matrix = matrix;
|
||||
|
||||
return qrCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decides the smallest version of QR code that will contain all of the provided data.
|
||||
/// </summary>
|
||||
/// <exception cref="WriterException">if the data cannot fit in any version</exception>
|
||||
private static Version recommendVersion(ErrorCorrectionLevel ecLevel, Mode mode, BitArray headerBits, BitArray dataBits)
|
||||
{
|
||||
// Hard part: need to know version to know how many bits length takes. But need to know how many
|
||||
// bits it takes to know version. First we take a guess at version by assuming version will be
|
||||
// the minimum, 1:
|
||||
var provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version.getVersionForNumber(1));
|
||||
var provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
|
||||
|
||||
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
|
||||
var bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion);
|
||||
return chooseVersion(bitsNeeded, ecLevel);
|
||||
}
|
||||
|
||||
private static int calculateBitsNeeded(Mode mode, BitArray headerBits, BitArray dataBits, Version version)
|
||||
{
|
||||
return headerBits.Size + mode.getCharacterCountBits(version) + dataBits.Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alphanumeric code.
|
||||
/// </summary>
|
||||
/// <param name="code">The code.</param>
|
||||
/// <returns>the code point of the table used in alphanumeric mode or
|
||||
/// -1 if there is no corresponding code in the table.</returns>
|
||||
internal static int getAlphanumericCode(int code)
|
||||
{
|
||||
if (code < ALPHANUMERIC_TABLE.Length)
|
||||
{
|
||||
return ALPHANUMERIC_TABLE[code];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chooses the mode.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns></returns>
|
||||
public static Mode chooseMode(String content)
|
||||
{
|
||||
return chooseMode(content, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
|
||||
/// if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <returns></returns>
|
||||
private static Mode chooseMode(String content, String encoding)
|
||||
{
|
||||
if ("Shift_JIS".Equals(encoding) && isOnlyDoubleByteKanji(content))
|
||||
{
|
||||
// Choose Kanji mode if all input are double-byte characters
|
||||
return Mode.KANJI;
|
||||
}
|
||||
bool hasNumeric = false;
|
||||
bool hasAlphanumeric = false;
|
||||
for (int i = 0; i < content.Length; ++i)
|
||||
{
|
||||
char c = content[i];
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
hasNumeric = true;
|
||||
}
|
||||
else if (getAlphanumericCode(c) != -1)
|
||||
{
|
||||
hasAlphanumeric = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Mode.BYTE;
|
||||
}
|
||||
}
|
||||
if (hasAlphanumeric)
|
||||
{
|
||||
|
||||
return Mode.ALPHANUMERIC;
|
||||
}
|
||||
if (hasNumeric)
|
||||
{
|
||||
|
||||
return Mode.NUMERIC;
|
||||
}
|
||||
return Mode.BYTE;
|
||||
}
|
||||
|
||||
private static bool isOnlyDoubleByteKanji(String content)
|
||||
{
|
||||
byte[] bytes;
|
||||
try
|
||||
{
|
||||
bytes = Encoding.GetEncoding("Shift_JIS").GetBytes(content);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int length = bytes.Length;
|
||||
if (length % 2 != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < length; i += 2)
|
||||
{
|
||||
|
||||
|
||||
int byte1 = bytes[i] & 0xFF;
|
||||
if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB))
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int chooseMaskPattern(BitArray bits,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
Version version,
|
||||
ByteMatrix matrix)
|
||||
{
|
||||
int minPenalty = Int32.MaxValue; // Lower penalty is better.
|
||||
int bestMaskPattern = -1;
|
||||
// We try all mask patterns to choose the best one.
|
||||
for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++)
|
||||
{
|
||||
|
||||
MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
|
||||
int penalty = calculateMaskPenalty(matrix);
|
||||
if (penalty < minPenalty)
|
||||
{
|
||||
|
||||
minPenalty = penalty;
|
||||
bestMaskPattern = maskPattern;
|
||||
}
|
||||
}
|
||||
return bestMaskPattern;
|
||||
}
|
||||
|
||||
private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel)
|
||||
{
|
||||
for (int versionNum = 1; versionNum <= 40; versionNum++)
|
||||
{
|
||||
var version = Version.getVersionForNumber(versionNum);
|
||||
if (willFit(numInputBits, version, ecLevel))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
}
|
||||
throw new WriterException("Data too big");
|
||||
}
|
||||
|
||||
/// <summary></summary>
|
||||
/// <returns>true if the number of input bits will fit in a code with the specified version and error correction level.</returns>
|
||||
private static bool willFit(int numInputBits, Version version, ErrorCorrectionLevel ecLevel)
|
||||
{
|
||||
// In the following comments, we use numbers of Version 7-H.
|
||||
// numBytes = 196
|
||||
var numBytes = version.TotalCodewords;
|
||||
// getNumECBytes = 130
|
||||
var ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
var numEcBytes = ecBlocks.TotalECCodewords;
|
||||
// getNumDataBytes = 196 - 130 = 66
|
||||
var numDataBytes = numBytes - numEcBytes;
|
||||
var totalInputBytes = (numInputBits + 7) / 8;
|
||||
return numDataBytes >= totalInputBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
|
||||
/// </summary>
|
||||
/// <param name="numDataBytes">The num data bytes.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
internal static void terminateBits(int numDataBytes, BitArray bits)
|
||||
{
|
||||
int capacity = numDataBytes << 3;
|
||||
if (bits.Size > capacity)
|
||||
{
|
||||
throw new WriterException("data bits cannot fit in the QR Code" + bits.Size + " > " +
|
||||
capacity);
|
||||
}
|
||||
for (int i = 0; i < 4 && bits.Size < capacity; ++i)
|
||||
{
|
||||
bits.appendBit(false);
|
||||
}
|
||||
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
|
||||
// If the last byte isn't 8-bit aligned, we'll add padding bits.
|
||||
int numBitsInLastByte = bits.Size & 0x07;
|
||||
if (numBitsInLastByte > 0)
|
||||
{
|
||||
for (int i = numBitsInLastByte; i < 8; i++)
|
||||
{
|
||||
bits.appendBit(false);
|
||||
}
|
||||
}
|
||||
// If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
|
||||
int numPaddingBytes = numDataBytes - bits.SizeInBytes;
|
||||
for (int i = 0; i < numPaddingBytes; ++i)
|
||||
{
|
||||
bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
|
||||
}
|
||||
if (bits.Size != capacity)
|
||||
{
|
||||
throw new WriterException("Bits size does not equal capacity");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get number of data bytes and number of error correction bytes for block id "blockID". Store
|
||||
/// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
|
||||
/// JISX0510:2004 (p.30)
|
||||
/// </summary>
|
||||
/// <param name="numTotalBytes">The num total bytes.</param>
|
||||
/// <param name="numDataBytes">The num data bytes.</param>
|
||||
/// <param name="numRSBlocks">The num RS blocks.</param>
|
||||
/// <param name="blockID">The block ID.</param>
|
||||
/// <param name="numDataBytesInBlock">The num data bytes in block.</param>
|
||||
/// <param name="numECBytesInBlock">The num EC bytes in block.</param>
|
||||
internal static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes,
|
||||
int numDataBytes,
|
||||
int numRSBlocks,
|
||||
int blockID,
|
||||
int[] numDataBytesInBlock,
|
||||
int[] numECBytesInBlock)
|
||||
{
|
||||
if (blockID >= numRSBlocks)
|
||||
{
|
||||
throw new WriterException("Block ID too large");
|
||||
}
|
||||
// numRsBlocksInGroup2 = 196 % 5 = 1
|
||||
int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
|
||||
// numRsBlocksInGroup1 = 5 - 1 = 4
|
||||
int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
|
||||
// numTotalBytesInGroup1 = 196 / 5 = 39
|
||||
int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
|
||||
// numTotalBytesInGroup2 = 39 + 1 = 40
|
||||
int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
|
||||
// numDataBytesInGroup1 = 66 / 5 = 13
|
||||
int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
|
||||
// numDataBytesInGroup2 = 13 + 1 = 14
|
||||
int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
|
||||
// numEcBytesInGroup1 = 39 - 13 = 26
|
||||
int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
|
||||
// numEcBytesInGroup2 = 40 - 14 = 26
|
||||
int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
|
||||
// Sanity checks.
|
||||
// 26 = 26
|
||||
if (numEcBytesInGroup1 != numEcBytesInGroup2)
|
||||
{
|
||||
|
||||
throw new WriterException("EC bytes mismatch");
|
||||
}
|
||||
// 5 = 4 + 1.
|
||||
if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2)
|
||||
{
|
||||
|
||||
throw new WriterException("RS blocks mismatch");
|
||||
}
|
||||
// 196 = (13 + 26) * 4 + (14 + 26) * 1
|
||||
if (numTotalBytes !=
|
||||
((numDataBytesInGroup1 + numEcBytesInGroup1) *
|
||||
numRsBlocksInGroup1) +
|
||||
((numDataBytesInGroup2 + numEcBytesInGroup2) *
|
||||
numRsBlocksInGroup2))
|
||||
{
|
||||
throw new WriterException("Total bytes mismatch");
|
||||
}
|
||||
|
||||
if (blockID < numRsBlocksInGroup1)
|
||||
{
|
||||
|
||||
numDataBytesInBlock[0] = numDataBytesInGroup1;
|
||||
numECBytesInBlock[0] = numEcBytesInGroup1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
numDataBytesInBlock[0] = numDataBytesInGroup2;
|
||||
numECBytesInBlock[0] = numEcBytesInGroup2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interleave "bits" with corresponding error correction bytes. On success, store the result in
|
||||
/// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
|
||||
/// </summary>
|
||||
/// <param name="bits">The bits.</param>
|
||||
/// <param name="numTotalBytes">The num total bytes.</param>
|
||||
/// <param name="numDataBytes">The num data bytes.</param>
|
||||
/// <param name="numRSBlocks">The num RS blocks.</param>
|
||||
/// <returns></returns>
|
||||
internal static BitArray interleaveWithECBytes(BitArray bits,
|
||||
int numTotalBytes,
|
||||
int numDataBytes,
|
||||
int numRSBlocks)
|
||||
{
|
||||
// "bits" must have "getNumDataBytes" bytes of data.
|
||||
if (bits.SizeInBytes != numDataBytes)
|
||||
{
|
||||
|
||||
throw new WriterException("Number of bits and data bytes does not match");
|
||||
}
|
||||
|
||||
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
|
||||
// store the divided data bytes blocks and error correction bytes blocks into "blocks".
|
||||
int dataBytesOffset = 0;
|
||||
int maxNumDataBytes = 0;
|
||||
int maxNumEcBytes = 0;
|
||||
|
||||
// Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
|
||||
var blocks = new List<BlockPair>(numRSBlocks);
|
||||
|
||||
for (int i = 0; i < numRSBlocks; ++i)
|
||||
{
|
||||
|
||||
int[] numDataBytesInBlock = new int[1];
|
||||
int[] numEcBytesInBlock = new int[1];
|
||||
getNumDataBytesAndNumECBytesForBlockID(
|
||||
numTotalBytes, numDataBytes, numRSBlocks, i,
|
||||
numDataBytesInBlock, numEcBytesInBlock);
|
||||
|
||||
int size = numDataBytesInBlock[0];
|
||||
byte[] dataBytes = new byte[size];
|
||||
bits.toBytes(8 * dataBytesOffset, dataBytes, 0, size);
|
||||
byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
|
||||
blocks.Add(new BlockPair(dataBytes, ecBytes));
|
||||
|
||||
maxNumDataBytes = Math.Max(maxNumDataBytes, size);
|
||||
maxNumEcBytes = Math.Max(maxNumEcBytes, ecBytes.Length);
|
||||
dataBytesOffset += numDataBytesInBlock[0];
|
||||
}
|
||||
if (numDataBytes != dataBytesOffset)
|
||||
{
|
||||
|
||||
throw new WriterException("Data bytes does not match offset");
|
||||
}
|
||||
|
||||
BitArray result = new BitArray();
|
||||
|
||||
// First, place data blocks.
|
||||
for (int i = 0; i < maxNumDataBytes; ++i)
|
||||
{
|
||||
foreach (BlockPair block in blocks)
|
||||
{
|
||||
byte[] dataBytes = block.DataBytes;
|
||||
if (i < dataBytes.Length)
|
||||
{
|
||||
result.appendBits(dataBytes[i], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then, place error correction blocks.
|
||||
for (int i = 0; i < maxNumEcBytes; ++i)
|
||||
{
|
||||
foreach (BlockPair block in blocks)
|
||||
{
|
||||
byte[] ecBytes = block.ErrorCorrectionBytes;
|
||||
if (i < ecBytes.Length)
|
||||
{
|
||||
result.appendBits(ecBytes[i], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numTotalBytes != result.SizeInBytes)
|
||||
{ // Should be same.
|
||||
throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
|
||||
result.SizeInBytes + " differ.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock)
|
||||
{
|
||||
int numDataBytes = dataBytes.Length;
|
||||
int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
|
||||
for (int i = 0; i < numDataBytes; i++)
|
||||
{
|
||||
toEncode[i] = dataBytes[i] & 0xFF;
|
||||
|
||||
}
|
||||
new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
|
||||
|
||||
byte[] ecBytes = new byte[numEcBytesInBlock];
|
||||
for (int i = 0; i < numEcBytesInBlock; i++)
|
||||
{
|
||||
ecBytes[i] = (byte)toEncode[numDataBytes + i];
|
||||
|
||||
}
|
||||
return ecBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append mode info. On success, store the result in "bits".
|
||||
/// </summary>
|
||||
/// <param name="mode">The mode.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
internal static void appendModeInfo(Mode mode, BitArray bits)
|
||||
{
|
||||
bits.appendBits(mode.Bits, 4);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Append length info. On success, store the result in "bits".
|
||||
/// </summary>
|
||||
/// <param name="numLetters">The num letters.</param>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="mode">The mode.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
internal static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits)
|
||||
{
|
||||
int numBits = mode.getCharacterCountBits(version);
|
||||
if (numLetters >= (1 << numBits))
|
||||
{
|
||||
throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1));
|
||||
}
|
||||
bits.appendBits(numLetters, numBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="mode">The mode.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
internal static void appendBytes(String content,
|
||||
Mode mode,
|
||||
BitArray bits,
|
||||
String encoding)
|
||||
{
|
||||
if (mode.Equals(Mode.NUMERIC))
|
||||
appendNumericBytes(content, bits);
|
||||
else
|
||||
if (mode.Equals(Mode.ALPHANUMERIC))
|
||||
appendAlphanumericBytes(content, bits);
|
||||
else
|
||||
if (mode.Equals(Mode.BYTE))
|
||||
append8BitBytes(content, bits, encoding);
|
||||
else
|
||||
if (mode.Equals(Mode.KANJI))
|
||||
appendKanjiBytes(content, bits);
|
||||
else
|
||||
throw new WriterException("Invalid mode: " + mode);
|
||||
}
|
||||
|
||||
internal static void appendNumericBytes(String content, BitArray bits)
|
||||
{
|
||||
int length = content.Length;
|
||||
|
||||
int i = 0;
|
||||
while (i < length)
|
||||
{
|
||||
int num1 = content[i] - '0';
|
||||
if (i + 2 < length)
|
||||
{
|
||||
// Encode three numeric letters in ten bits.
|
||||
int num2 = content[i + 1] - '0';
|
||||
int num3 = content[i + 2] - '0';
|
||||
bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
|
||||
i += 3;
|
||||
}
|
||||
else if (i + 1 < length)
|
||||
{
|
||||
// Encode two numeric letters in seven bits.
|
||||
int num2 = content[i + 1] - '0';
|
||||
bits.appendBits(num1 * 10 + num2, 7);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encode one numeric letter in four bits.
|
||||
bits.appendBits(num1, 4);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void appendAlphanumericBytes(String content, BitArray bits)
|
||||
{
|
||||
int length = content.Length;
|
||||
|
||||
int i = 0;
|
||||
while (i < length)
|
||||
{
|
||||
int code1 = getAlphanumericCode(content[i]);
|
||||
if (code1 == -1)
|
||||
{
|
||||
throw new WriterException();
|
||||
}
|
||||
if (i + 1 < length)
|
||||
{
|
||||
int code2 = getAlphanumericCode(content[i + 1]);
|
||||
if (code2 == -1)
|
||||
{
|
||||
throw new WriterException();
|
||||
}
|
||||
// Encode two alphanumeric letters in 11 bits.
|
||||
bits.appendBits(code1 * 45 + code2, 11);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encode one alphanumeric letter in six bits.
|
||||
bits.appendBits(code1, 6);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void append8BitBytes(String content, BitArray bits, String encoding)
|
||||
{
|
||||
byte[] bytes;
|
||||
try
|
||||
{
|
||||
bytes = Encoding.GetEncoding(encoding).GetBytes(content);
|
||||
}
|
||||
#if WindowsCE
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// WindowsCE doesn't support all encodings. But it is device depended.
|
||||
// So we try here the some different ones
|
||||
if (encoding == "ISO-8859-1")
|
||||
{
|
||||
bytes = Encoding.GetEncoding(1252).GetBytes(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = Encoding.GetEncoding("UTF-8").GetBytes(content);
|
||||
}
|
||||
}
|
||||
catch (Exception uee)
|
||||
{
|
||||
throw new WriterException(uee.Message, uee);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
catch (Exception uee)
|
||||
{
|
||||
throw new WriterException(uee.Message, uee);
|
||||
}
|
||||
foreach (byte b in bytes)
|
||||
{
|
||||
bits.appendBits(b, 8);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void appendKanjiBytes(String content, BitArray bits)
|
||||
{
|
||||
byte[] bytes;
|
||||
try
|
||||
{
|
||||
bytes = Encoding.GetEncoding("Shift_JIS").GetBytes(content);
|
||||
}
|
||||
catch (Exception uee)
|
||||
{
|
||||
throw new WriterException(uee.Message, uee);
|
||||
}
|
||||
if (bytes.Length % 2 != 0)
|
||||
{
|
||||
throw new WriterException("Kanji byte size not even");
|
||||
}
|
||||
int maxI = bytes.Length - 1; // bytes.length must be even
|
||||
for (int i = 0; i < maxI; i += 2)
|
||||
{
|
||||
int byte1 = bytes[i] & 0xFF;
|
||||
int byte2 = bytes[i + 1] & 0xFF;
|
||||
int code = (byte1 << 8) | byte2;
|
||||
int subtracted = -1;
|
||||
if (code >= 0x8140 && code <= 0x9ffc)
|
||||
{
|
||||
|
||||
subtracted = code - 0x8140;
|
||||
}
|
||||
else if (code >= 0xe040 && code <= 0xebbf)
|
||||
{
|
||||
subtracted = code - 0xc140;
|
||||
}
|
||||
if (subtracted == -1)
|
||||
{
|
||||
|
||||
throw new WriterException("Invalid byte sequence");
|
||||
}
|
||||
int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
|
||||
bits.appendBits(encoded, 13);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendECI(CharacterSetECI eci, BitArray bits)
|
||||
{
|
||||
bits.appendBits(Mode.ECI.Bits, 4);
|
||||
|
||||
// This is correct for values up to 127, which is all we need now.
|
||||
bits.appendBits(eci.Value, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
277
Others/zxing.core/zxing.core/qrcode/encoder/MaskUtil.cs
Normal file
277
Others/zxing.core/zxing.core/qrcode/encoder/MaskUtil.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2008 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;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author>Satoru Takabayashi</author>
|
||||
/// <author>Daniel Switkin</author>
|
||||
/// <author>Sean Owen</author>
|
||||
public static class MaskUtil
|
||||
{
|
||||
// Penalty weights from section 6.8.2.1
|
||||
private const int N1 = 3;
|
||||
private const int N2 = 3;
|
||||
private const int N3 = 40;
|
||||
private const int N4 = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
|
||||
/// give penalty to them. Example: 00000 or 11111.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <returns></returns>
|
||||
public static int applyMaskPenaltyRule1(ByteMatrix matrix)
|
||||
{
|
||||
return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
|
||||
/// penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
|
||||
/// penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <returns></returns>
|
||||
public static int applyMaskPenaltyRule2(ByteMatrix matrix)
|
||||
{
|
||||
int penalty = 0;
|
||||
var array = matrix.Array;
|
||||
int width = matrix.Width;
|
||||
int height = matrix.Height;
|
||||
for (int y = 0; y < height - 1; y++)
|
||||
{
|
||||
var arrayY = array[y];
|
||||
var arrayY1 = array[y + 1];
|
||||
for (int x = 0; x < width - 1; x++)
|
||||
{
|
||||
int value = arrayY[x];
|
||||
if (value == arrayY[x + 1] && value == arrayY1[x] && value == arrayY1[x + 1])
|
||||
{
|
||||
penalty++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return N2 * penalty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
|
||||
/// 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
|
||||
/// penalties twice (i.e. 40 * 2).
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <returns></returns>
|
||||
public static int applyMaskPenaltyRule3(ByteMatrix matrix)
|
||||
{
|
||||
int numPenalties = 0;
|
||||
byte[][] array = matrix.Array;
|
||||
int width = matrix.Width;
|
||||
int height = matrix.Height;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
byte[] arrayY = array[y]; // We can at least optimize this access
|
||||
if (x + 6 < width &&
|
||||
arrayY[x] == 1 &&
|
||||
arrayY[x + 1] == 0 &&
|
||||
arrayY[x + 2] == 1 &&
|
||||
arrayY[x + 3] == 1 &&
|
||||
arrayY[x + 4] == 1 &&
|
||||
arrayY[x + 5] == 0 &&
|
||||
arrayY[x + 6] == 1 &&
|
||||
(isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11)))
|
||||
{
|
||||
numPenalties++;
|
||||
}
|
||||
if (y + 6 < height &&
|
||||
array[y][x] == 1 &&
|
||||
array[y + 1][x] == 0 &&
|
||||
array[y + 2][x] == 1 &&
|
||||
array[y + 3][x] == 1 &&
|
||||
array[y + 4][x] == 1 &&
|
||||
array[y + 5][x] == 0 &&
|
||||
array[y + 6][x] == 1 &&
|
||||
(isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11)))
|
||||
{
|
||||
numPenalties++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return numPenalties * N3;
|
||||
}
|
||||
|
||||
private static bool isWhiteHorizontal(byte[] rowArray, int from, int to)
|
||||
{
|
||||
from = Math.Max(from, 0);
|
||||
to = Math.Min(to, rowArray.Length);
|
||||
for (int i = from; i < to; i++)
|
||||
{
|
||||
if (rowArray[i] == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool isWhiteVertical(byte[][] array, int col, int from, int to)
|
||||
{
|
||||
from = Math.Max(from, 0);
|
||||
to = Math.Min(to, array.Length);
|
||||
for (int i = from; i < to; i++)
|
||||
{
|
||||
if (array[i][col] == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
|
||||
/// penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <returns></returns>
|
||||
public static int applyMaskPenaltyRule4(ByteMatrix matrix)
|
||||
{
|
||||
int numDarkCells = 0;
|
||||
var array = matrix.Array;
|
||||
int width = matrix.Width;
|
||||
int height = matrix.Height;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
var arrayY = array[y];
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (arrayY[x] == 1)
|
||||
{
|
||||
numDarkCells++;
|
||||
}
|
||||
}
|
||||
}
|
||||
var numTotalCells = matrix.Height * matrix.Width;
|
||||
var darkRatio = (double)numDarkCells / numTotalCells;
|
||||
var fivePercentVariances = (int)(Math.Abs(darkRatio - 0.5) * 20.0); // * 100.0 / 5.0
|
||||
return fivePercentVariances * N4;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
|
||||
/// pattern conditions.
|
||||
/// </summary>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns></returns>
|
||||
public static bool getDataMaskBit(int maskPattern, int x, int y)
|
||||
{
|
||||
int intermediate, temp;
|
||||
switch (maskPattern)
|
||||
{
|
||||
|
||||
case 0:
|
||||
intermediate = (y + x) & 0x1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
intermediate = y & 0x1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
intermediate = x % 3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
intermediate = (y + x) % 3;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
intermediate = (((int)((uint)y >> 1)) + (x / 3)) & 0x1;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
temp = y * x;
|
||||
intermediate = (temp & 0x1) + (temp % 3);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
temp = y * x;
|
||||
intermediate = (((temp & 0x1) + (temp % 3)) & 0x1);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
temp = y * x;
|
||||
intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Invalid mask pattern: " + maskPattern);
|
||||
|
||||
}
|
||||
return intermediate == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
|
||||
/// vertical and horizontal orders respectively.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <param name="isHorizontal">if set to <c>true</c> [is horizontal].</param>
|
||||
/// <returns></returns>
|
||||
private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, bool isHorizontal)
|
||||
{
|
||||
int penalty = 0;
|
||||
int iLimit = isHorizontal ? matrix.Height : matrix.Width;
|
||||
int jLimit = isHorizontal ? matrix.Width : matrix.Height;
|
||||
var array = matrix.Array;
|
||||
for (int i = 0; i < iLimit; i++)
|
||||
{
|
||||
int numSameBitCells = 0;
|
||||
int prevBit = -1;
|
||||
for (int j = 0; j < jLimit; j++)
|
||||
{
|
||||
int bit = isHorizontal ? array[i][j] : array[j][i];
|
||||
if (bit == prevBit)
|
||||
{
|
||||
numSameBitCells++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numSameBitCells >= 5)
|
||||
{
|
||||
penalty += N1 + (numSameBitCells - 5);
|
||||
}
|
||||
numSameBitCells = 1; // Include the cell itself.
|
||||
prevBit = bit;
|
||||
}
|
||||
}
|
||||
if (numSameBitCells >= 5)
|
||||
{
|
||||
penalty += N1 + (numSameBitCells - 5);
|
||||
}
|
||||
}
|
||||
return penalty;
|
||||
}
|
||||
}
|
||||
}
|
||||
603
Others/zxing.core/zxing.core/qrcode/encoder/MatrixUtil.cs
Normal file
603
Others/zxing.core/zxing.core/qrcode/encoder/MatrixUtil.cs
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Copyright 2008 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 ZXing.Common;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <author>
|
||||
/// satorux@google.com (Satoru Takabayashi) - creator
|
||||
/// </author>
|
||||
public static class MatrixUtil
|
||||
{
|
||||
private static readonly int[][] POSITION_DETECTION_PATTERN = new int[][]
|
||||
{
|
||||
new int[] {1, 1, 1, 1, 1, 1, 1},
|
||||
new int[] {1, 0, 0, 0, 0, 0, 1},
|
||||
new int[] {1, 0, 1, 1, 1, 0, 1},
|
||||
new int[] {1, 0, 1, 1, 1, 0, 1},
|
||||
new int[] {1, 0, 1, 1, 1, 0, 1},
|
||||
new int[] {1, 0, 0, 0, 0, 0, 1},
|
||||
new int[] {1, 1, 1, 1, 1, 1, 1}
|
||||
};
|
||||
|
||||
private static readonly int[][] POSITION_ADJUSTMENT_PATTERN = new int[][]
|
||||
{
|
||||
new int[] {1, 1, 1, 1, 1},
|
||||
new int[] {1, 0, 0, 0, 1},
|
||||
new int[] {1, 0, 1, 0, 1},
|
||||
new int[] {1, 0, 0, 0, 1},
|
||||
new int[] {1, 1, 1, 1, 1}
|
||||
};
|
||||
|
||||
// From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
|
||||
private static readonly int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = new int[][]
|
||||
{
|
||||
new int[] {-1, -1, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 18, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 22, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 26, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 30, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 34, -1, -1, -1, -1, -1},
|
||||
new int[] {6, 22, 38, -1, -1, -1, -1},
|
||||
new int[] {6, 24, 42, -1, -1, -1, -1},
|
||||
new int[] {6, 26, 46, -1, -1, -1, -1},
|
||||
new int[] {6, 28, 50, -1, -1, -1, -1},
|
||||
new int[] {6, 30, 54, -1, -1, -1, -1},
|
||||
new int[] {6, 32, 58, -1, -1, -1, -1},
|
||||
new int[] {6, 34, 62, -1, -1, -1, -1},
|
||||
new int[] {6, 26, 46, 66, -1, -1, -1},
|
||||
new int[] {6, 26, 48, 70, -1, -1, -1},
|
||||
new int[] {6, 26, 50, 74, -1, -1, -1},
|
||||
new int[] {6, 30, 54, 78, -1, -1, -1},
|
||||
new int[] {6, 30, 56, 82, -1, -1, -1},
|
||||
new int[] {6, 30, 58, 86, -1, -1, -1},
|
||||
new int[] {6, 34, 62, 90, -1, -1, -1},
|
||||
new int[] {6, 28, 50, 72, 94, -1, -1},
|
||||
new int[] {6, 26, 50, 74, 98, -1, -1},
|
||||
new int[] {6, 30, 54, 78, 102, -1, -1},
|
||||
new int[] {6, 28, 54, 80, 106, -1, -1},
|
||||
new int[] {6, 32, 58, 84, 110, -1, -1},
|
||||
new int[] {6, 30, 58, 86, 114, -1, -1},
|
||||
new int[] {6, 34, 62, 90, 118, -1, -1},
|
||||
new int[] {6, 26, 50, 74, 98, 122, -1},
|
||||
new int[] {6, 30, 54, 78, 102, 126, -1},
|
||||
new int[] {6, 26, 52, 78, 104, 130, -1},
|
||||
new int[] {6, 30, 56, 82, 108, 134, -1},
|
||||
new int[] {6, 34, 60, 86, 112, 138, -1},
|
||||
new int[] {6, 30, 58, 86, 114, 142, -1},
|
||||
new int[] {6, 34, 62, 90, 118, 146, -1},
|
||||
new int[] {6, 30, 54, 78, 102, 126, 150},
|
||||
new int[] {6, 24, 50, 76, 102, 128, 154},
|
||||
new int[] {6, 28, 54, 80, 106, 132, 158},
|
||||
new int[] {6, 32, 58, 84, 110, 136, 162},
|
||||
new int[] {6, 26, 54, 82, 110, 138, 166},
|
||||
new int[] {6, 30, 58, 86, 114, 142, 170}
|
||||
};
|
||||
|
||||
// Type info cells at the left top corner.
|
||||
private static readonly int[][] TYPE_INFO_COORDINATES = new int[][]
|
||||
{
|
||||
new int[] {8, 0},
|
||||
new int[] {8, 1},
|
||||
new int[] {8, 2},
|
||||
new int[] {8, 3},
|
||||
new int[] {8, 4},
|
||||
new int[] {8, 5},
|
||||
new int[] {8, 7},
|
||||
new int[] {8, 8},
|
||||
new int[] {7, 8},
|
||||
new int[] {5, 8},
|
||||
new int[] {4, 8},
|
||||
new int[] {3, 8},
|
||||
new int[] {2, 8},
|
||||
new int[] {1, 8},
|
||||
new int[] {0, 8}
|
||||
};
|
||||
|
||||
// From Appendix D in JISX0510:2004 (p. 67)
|
||||
private const int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
|
||||
|
||||
// From Appendix C in JISX0510:2004 (p.65).
|
||||
private const int TYPE_INFO_POLY = 0x537;
|
||||
private const int TYPE_INFO_MASK_PATTERN = 0x5412;
|
||||
|
||||
/// <summary>
|
||||
/// Set all cells to 2. 2 means that the cell is empty (not set yet).
|
||||
///
|
||||
/// JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
|
||||
/// with the ByteMatrix initialized all to zero.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void clearMatrix(ByteMatrix matrix)
|
||||
{
|
||||
matrix.clear(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
|
||||
/// success, store the result in "matrix" and return true.
|
||||
/// </summary>
|
||||
/// <param name="dataBits">The data bits.</param>
|
||||
/// <param name="ecLevel">The ec level.</param>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, Version version, int maskPattern,
|
||||
ByteMatrix matrix)
|
||||
{
|
||||
clearMatrix(matrix);
|
||||
embedBasicPatterns(version, matrix);
|
||||
// Type information appear with any version.
|
||||
embedTypeInfo(ecLevel, maskPattern, matrix);
|
||||
// Version info appear if version >= 7.
|
||||
maybeEmbedVersionInfo(version, matrix);
|
||||
// Data should be embedded at end.
|
||||
embedDataBits(dataBits, maskPattern, matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed basic patterns. On success, modify the matrix and return true.
|
||||
/// The basic patterns are:
|
||||
/// - Position detection patterns
|
||||
/// - Timing patterns
|
||||
/// - Dark dot at the left bottom corner
|
||||
/// - Position adjustment patterns, if need be
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void embedBasicPatterns(Version version, ByteMatrix matrix)
|
||||
{
|
||||
// Let's get started with embedding big squares at corners.
|
||||
embedPositionDetectionPatternsAndSeparators(matrix);
|
||||
// Then, embed the dark dot at the left bottom corner.
|
||||
embedDarkDotAtLeftBottomCorner(matrix);
|
||||
|
||||
// Position adjustment patterns appear if version >= 2.
|
||||
maybeEmbedPositionAdjustmentPatterns(version, matrix);
|
||||
// Timing patterns should be embedded after position adj. patterns.
|
||||
embedTimingPatterns(matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed type information. On success, modify the matrix.
|
||||
/// </summary>
|
||||
/// <param name="ecLevel">The ec level.</param>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
|
||||
{
|
||||
BitArray typeInfoBits = new BitArray();
|
||||
makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
|
||||
|
||||
for (int i = 0; i < typeInfoBits.Size; ++i)
|
||||
{
|
||||
// Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
|
||||
// "typeInfoBits".
|
||||
int bit = typeInfoBits[typeInfoBits.Size - 1 - i] ? 1 : 0;
|
||||
|
||||
// Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
|
||||
int[] coordinates = TYPE_INFO_COORDINATES[i];
|
||||
int x1 = coordinates[0];
|
||||
int y1 = coordinates[1];
|
||||
matrix[x1, y1] = bit;
|
||||
|
||||
int x2;
|
||||
int y2;
|
||||
if (i < 8)
|
||||
{
|
||||
// Right top corner.
|
||||
x2 = matrix.Width - i - 1;
|
||||
y2 = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Left bottom corner.
|
||||
x2 = 8;
|
||||
y2 = matrix.Height - 7 + (i - 8);
|
||||
}
|
||||
matrix[x2, y2] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed version information if need be. On success, modify the matrix and return true.
|
||||
/// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix)
|
||||
{
|
||||
if (version.VersionNumber < 7)
|
||||
{
|
||||
// Version info is necessary if version >= 7.
|
||||
return; // Don't need version info.
|
||||
}
|
||||
BitArray versionInfoBits = new BitArray();
|
||||
makeVersionInfoBits(version, versionInfoBits);
|
||||
|
||||
int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
// Place bits in LSB (least significant bit) to MSB order.
|
||||
var bit = versionInfoBits[bitIndex] ? 1 : 0;
|
||||
bitIndex--;
|
||||
// Left bottom corner.
|
||||
matrix[i, matrix.Height - 11 + j] = bit;
|
||||
// Right bottom corner.
|
||||
matrix[matrix.Height - 11 + j, i] = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
|
||||
/// For debugging purposes, it skips masking process if "getMaskPattern" is -1.
|
||||
/// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
|
||||
/// </summary>
|
||||
/// <param name="dataBits">The data bits.</param>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
|
||||
{
|
||||
int bitIndex = 0;
|
||||
int direction = -1;
|
||||
// Start from the right bottom cell.
|
||||
int x = matrix.Width - 1;
|
||||
int y = matrix.Height - 1;
|
||||
while (x > 0)
|
||||
{
|
||||
// Skip the vertical timing pattern.
|
||||
if (x == 6)
|
||||
{
|
||||
x -= 1;
|
||||
}
|
||||
while (y >= 0 && y < matrix.Height)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
int xx = x - i;
|
||||
// Skip the cell if it's not empty.
|
||||
if (!isEmpty(matrix[xx, y]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int bit;
|
||||
if (bitIndex < dataBits.Size)
|
||||
{
|
||||
bit = dataBits[bitIndex] ? 1 : 0;
|
||||
++bitIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
|
||||
// in 8.4.9 of JISX0510:2004 (p. 24).
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
// Skip masking if mask_pattern is -1.
|
||||
if (maskPattern != -1)
|
||||
{
|
||||
if (MaskUtil.getDataMaskBit(maskPattern, xx, y))
|
||||
{
|
||||
bit ^= 0x1;
|
||||
}
|
||||
}
|
||||
matrix[xx, y] = bit;
|
||||
}
|
||||
y += direction;
|
||||
}
|
||||
direction = -direction; // Reverse the direction.
|
||||
y += direction;
|
||||
x -= 2; // Move to the left.
|
||||
}
|
||||
// All bits should be consumed.
|
||||
if (bitIndex != dataBits.Size)
|
||||
{
|
||||
throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.Size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the position of the most significant bit set (to one) in the "value". The most
|
||||
/// significant bit is position 32. If there is no bit set, return 0. Examples:
|
||||
/// - findMSBSet(0) => 0
|
||||
/// - findMSBSet(1) => 1
|
||||
/// - findMSBSet(255) => 8
|
||||
/// </summary>
|
||||
/// <param name="value_Renamed">The value_ renamed.</param>
|
||||
/// <returns></returns>
|
||||
public static int findMSBSet(int value_Renamed)
|
||||
{
|
||||
int numDigits = 0;
|
||||
while (value_Renamed != 0)
|
||||
{
|
||||
value_Renamed = (int)((uint)value_Renamed >> 1);
|
||||
++numDigits;
|
||||
}
|
||||
return numDigits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
|
||||
/// code is used for encoding type information and version information.
|
||||
/// Example: Calculation of version information of 7.
|
||||
/// f(x) is created from 7.
|
||||
/// - 7 = 000111 in 6 bits
|
||||
/// - f(x) = x^2 + x^2 + x^1
|
||||
/// g(x) is given by the standard (p. 67)
|
||||
/// - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
|
||||
/// Multiply f(x) by x^(18 - 6)
|
||||
/// - f'(x) = f(x) * x^(18 - 6)
|
||||
/// - f'(x) = x^14 + x^13 + x^12
|
||||
/// Calculate the remainder of f'(x) / g(x)
|
||||
/// x^2
|
||||
/// __________________________________________________
|
||||
/// g(x) )x^14 + x^13 + x^12
|
||||
/// x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
|
||||
/// --------------------------------------------------
|
||||
/// x^11 + x^10 + x^7 + x^4 + x^2
|
||||
///
|
||||
/// The remainder is x^11 + x^10 + x^7 + x^4 + x^2
|
||||
/// Encode it in binary: 110010010100
|
||||
/// The return value is 0xc94 (1100 1001 0100)
|
||||
///
|
||||
/// Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
|
||||
/// operations. We don't care if coefficients are positive or negative.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="poly">The poly.</param>
|
||||
/// <returns></returns>
|
||||
public static int calculateBCHCode(int value, int poly)
|
||||
{
|
||||
if (poly == 0)
|
||||
throw new ArgumentException("0 polynominal", "poly");
|
||||
|
||||
// If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
|
||||
// from 13 to make it 12.
|
||||
int msbSetInPoly = findMSBSet(poly);
|
||||
value <<= msbSetInPoly - 1;
|
||||
// Do the division business using exclusive-or operations.
|
||||
while (findMSBSet(value) >= msbSetInPoly)
|
||||
{
|
||||
value ^= poly << (findMSBSet(value) - msbSetInPoly);
|
||||
}
|
||||
// Now the "value" is the remainder (i.e. the BCH code)
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make bit vector of type information. On success, store the result in "bits" and return true.
|
||||
/// Encode error correction level and mask pattern. See 8.9 of
|
||||
/// JISX0510:2004 (p.45) for details.
|
||||
/// </summary>
|
||||
/// <param name="ecLevel">The ec level.</param>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
|
||||
{
|
||||
if (!QRCode.isValidMaskPattern(maskPattern))
|
||||
{
|
||||
throw new WriterException("Invalid mask pattern");
|
||||
}
|
||||
int typeInfo = (ecLevel.Bits << 3) | maskPattern;
|
||||
bits.appendBits(typeInfo, 5);
|
||||
|
||||
int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
|
||||
bits.appendBits(bchCode, 10);
|
||||
|
||||
BitArray maskBits = new BitArray();
|
||||
maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
|
||||
bits.xor(maskBits);
|
||||
|
||||
if (bits.Size != 15)
|
||||
{
|
||||
// Just in case.
|
||||
throw new WriterException("should not happen but we got: " + bits.Size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make bit vector of version information. On success, store the result in "bits" and return true.
|
||||
/// See 8.10 of JISX0510:2004 (p.45) for details.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="bits">The bits.</param>
|
||||
public static void makeVersionInfoBits(Version version, BitArray bits)
|
||||
{
|
||||
bits.appendBits(version.VersionNumber, 6);
|
||||
int bchCode = calculateBCHCode(version.VersionNumber, VERSION_INFO_POLY);
|
||||
bits.appendBits(bchCode, 12);
|
||||
|
||||
if (bits.Size != 18)
|
||||
{
|
||||
// Just in case.
|
||||
throw new WriterException("should not happen but we got: " + bits.Size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if "value" is empty.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified value is empty; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
private static bool isEmpty(int value)
|
||||
{
|
||||
return value == 2;
|
||||
}
|
||||
|
||||
private static void embedTimingPatterns(ByteMatrix matrix)
|
||||
{
|
||||
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
|
||||
// separation patterns (size 1). Thus, 8 = 7 + 1.
|
||||
for (int i = 8; i < matrix.Width - 8; ++i)
|
||||
{
|
||||
int bit = (i + 1) % 2;
|
||||
// Horizontal line.
|
||||
if (isEmpty(matrix[i, 6]))
|
||||
{
|
||||
matrix[i, 6] = bit;
|
||||
}
|
||||
// Vertical line.
|
||||
if (isEmpty(matrix[6, i]))
|
||||
{
|
||||
matrix[6, i] = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix)
|
||||
{
|
||||
if (matrix[8, matrix.Height - 8] == 0)
|
||||
{
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix[8, matrix.Height - 8] = 1;
|
||||
}
|
||||
|
||||
private static void embedHorizontalSeparationPattern(int xStart, int yStart, ByteMatrix matrix)
|
||||
{
|
||||
for (int x = 0; x < 8; ++x)
|
||||
{
|
||||
if (!isEmpty(matrix[xStart + x, yStart]))
|
||||
{
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix[xStart + x, yStart] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void embedVerticalSeparationPattern(int xStart, int yStart, ByteMatrix matrix)
|
||||
{
|
||||
for (int y = 0; y < 7; ++y)
|
||||
{
|
||||
if (!isEmpty(matrix[xStart, yStart + y]))
|
||||
{
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix[xStart, yStart + y] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="xStart">The x start.</param>
|
||||
/// <param name="yStart">The y start.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix)
|
||||
{
|
||||
for (int y = 0; y < 5; ++y)
|
||||
{
|
||||
var patternY = POSITION_ADJUSTMENT_PATTERN[y];
|
||||
for (int x = 0; x < 5; ++x)
|
||||
{
|
||||
matrix[xStart + x, yStart + y] = patternY[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix)
|
||||
{
|
||||
for (int y = 0; y < 7; ++y)
|
||||
{
|
||||
var patternY = POSITION_DETECTION_PATTERN[y];
|
||||
for (int x = 0; x < 7; ++x)
|
||||
{
|
||||
matrix[xStart + x, yStart + y] = patternY[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed position detection patterns and surrounding vertical/horizontal separators.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix)
|
||||
{
|
||||
// Embed three big squares at corners.
|
||||
int pdpWidth = POSITION_DETECTION_PATTERN[0].Length;
|
||||
// Left top corner.
|
||||
embedPositionDetectionPattern(0, 0, matrix);
|
||||
// Right top corner.
|
||||
embedPositionDetectionPattern(matrix.Width - pdpWidth, 0, matrix);
|
||||
// Left bottom corner.
|
||||
embedPositionDetectionPattern(0, matrix.Width - pdpWidth, matrix);
|
||||
|
||||
// Embed horizontal separation patterns around the squares.
|
||||
const int hspWidth = 8;
|
||||
// Left top corner.
|
||||
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
|
||||
// Right top corner.
|
||||
embedHorizontalSeparationPattern(matrix.Width - hspWidth, hspWidth - 1, matrix);
|
||||
// Left bottom corner.
|
||||
embedHorizontalSeparationPattern(0, matrix.Width - hspWidth, matrix);
|
||||
|
||||
// Embed vertical separation patterns around the squares.
|
||||
const int vspSize = 7;
|
||||
// Left top corner.
|
||||
embedVerticalSeparationPattern(vspSize, 0, matrix);
|
||||
// Right top corner.
|
||||
embedVerticalSeparationPattern(matrix.Height - vspSize - 1, 0, matrix);
|
||||
// Left bottom corner.
|
||||
embedVerticalSeparationPattern(vspSize, matrix.Height - vspSize, matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embed position adjustment patterns if need be.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix)
|
||||
{
|
||||
if (version.VersionNumber < 2)
|
||||
{
|
||||
// The patterns appear if version >= 2
|
||||
return;
|
||||
}
|
||||
int index = version.VersionNumber - 1;
|
||||
int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
|
||||
foreach (int y in coordinates)
|
||||
{
|
||||
if (y >= 0)
|
||||
{
|
||||
foreach (int x in coordinates)
|
||||
{
|
||||
if (x >= 0 && isEmpty(matrix[x, y]))
|
||||
{
|
||||
// If the cell is unset, we embed the position adjustment pattern here.
|
||||
// -2 is necessary since the x/y coordinates point to the center of the pattern, not the
|
||||
// left top corner.
|
||||
embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
Others/zxing.core/zxing.core/qrcode/encoder/QRCode.cs
Normal file
125
Others/zxing.core/zxing.core/qrcode/encoder/QRCode.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2008 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.Text;
|
||||
|
||||
namespace ZXing.QrCode.Internal
|
||||
{
|
||||
/// <author>satorux@google.com (Satoru Takabayashi) - creator</author>
|
||||
/// <author>dswitkin@google.com (Daniel Switkin) - ported from C++</author>
|
||||
public sealed class QRCode
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static int NUM_MASK_PATTERNS = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QRCode"/> class.
|
||||
/// </summary>
|
||||
public QRCode()
|
||||
{
|
||||
MaskPattern = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mode.
|
||||
/// </value>
|
||||
public Mode Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EC level.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The EC level.
|
||||
/// </value>
|
||||
public ErrorCorrectionLevel ECLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The version.
|
||||
/// </value>
|
||||
public Version Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mask pattern.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mask pattern.
|
||||
/// </value>
|
||||
public int MaskPattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matrix.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matrix.
|
||||
/// </value>
|
||||
public ByteMatrix Matrix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String"/> that represents this instance.
|
||||
/// </returns>
|
||||
public override String ToString()
|
||||
{
|
||||
var result = new StringBuilder(200);
|
||||
result.Append("<<\n");
|
||||
result.Append(" mode: ");
|
||||
result.Append(Mode);
|
||||
result.Append("\n ecLevel: ");
|
||||
result.Append(ECLevel);
|
||||
result.Append("\n version: ");
|
||||
if (Version == null)
|
||||
result.Append("null");
|
||||
else
|
||||
result.Append(Version);
|
||||
result.Append("\n maskPattern: ");
|
||||
result.Append(MaskPattern);
|
||||
if (Matrix == null)
|
||||
{
|
||||
result.Append("\n matrix: null\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append("\n matrix:\n");
|
||||
result.Append(Matrix.ToString());
|
||||
}
|
||||
result.Append(">>\n");
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if "mask_pattern" is valid.
|
||||
/// </summary>
|
||||
/// <param name="maskPattern">The mask pattern.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if [is valid mask pattern] [the specified mask pattern]; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool isValidMaskPattern(int maskPattern)
|
||||
{
|
||||
return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2012 ZXing.Net 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.ComponentModel;
|
||||
using System.Globalization;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode.Internal;
|
||||
|
||||
namespace ZXing.QrCode
|
||||
{
|
||||
/// <summary>
|
||||
/// The class holds the available options for the QrCodeWriter
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class QrCodeEncodingOptions : EncodingOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies what degree of error correction to use, for example in QR Codes.
|
||||
/// Type depends on the encoder. For example for QR codes it's type
|
||||
/// <see cref="ErrorCorrectionLevel"/>.
|
||||
/// </summary>
|
||||
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
|
||||
[TypeConverter(typeof(ErrorLevelConverter))]
|
||||
[CategoryAttribute("Standard"), DescriptionAttribute("Specifies what degree of error correction to use.")]
|
||||
#endif
|
||||
public ErrorCorrectionLevel ErrorCorrection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
|
||||
{
|
||||
return (ErrorCorrectionLevel)Hints[EncodeHintType.ERROR_CORRECTION];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
|
||||
Hints.Remove(EncodeHintType.ERROR_CORRECTION);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hints[EncodeHintType.ERROR_CORRECTION] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies what character encoding to use where applicable (type <see cref="String"/>)
|
||||
/// </summary>
|
||||
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
|
||||
[CategoryAttribute("Standard"), DescriptionAttribute("Specifies what character encoding to " +
|
||||
"use where applicable.")]
|
||||
#endif
|
||||
public string CharacterSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.CHARACTER_SET))
|
||||
{
|
||||
return (string)Hints[EncodeHintType.CHARACTER_SET];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.CHARACTER_SET))
|
||||
Hints.Remove(EncodeHintType.CHARACTER_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hints[EncodeHintType.CHARACTER_SET] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly disables ECI segment when generating QR Code
|
||||
/// That is against the specification of QR Code but some
|
||||
/// readers have problems if the charset is switched from
|
||||
/// ISO-8859-1 (default) to UTF-8 with the necessary ECI segment.
|
||||
/// If you set the property to true you can use UTF-8 encoding
|
||||
/// and the ECI segment is omitted.
|
||||
/// </summary>
|
||||
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
|
||||
[CategoryAttribute("Standard"), DescriptionAttribute("Explicitly disables ECI segment when generating QR Code." +
|
||||
"That is against the specification but some readers have problems otherwise when switching charset to UTF-8.")]
|
||||
#endif
|
||||
public bool DisableECI
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.DISABLE_ECI))
|
||||
{
|
||||
return (bool)Hints[EncodeHintType.DISABLE_ECI];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
set
|
||||
{
|
||||
Hints[EncodeHintType.DISABLE_ECI] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the exact version of QR code to be encoded. An integer, range 1 to 40. If the data specified
|
||||
/// cannot fit within the required version, a WriterException will be thrown.
|
||||
/// </summary>
|
||||
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
|
||||
[CategoryAttribute("Standard"), DescriptionAttribute("Specifies the exact version of QR code to be encoded. " +
|
||||
"An integer, range 1 to 40. If the data specified cannot fit within the required version, " +
|
||||
"a WriterException will be thrown.")]
|
||||
#endif
|
||||
public int? QrVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.QR_VERSION))
|
||||
{
|
||||
return (int)Hints[EncodeHintType.QR_VERSION];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (Hints.ContainsKey(EncodeHintType.QR_VERSION))
|
||||
Hints.Remove(EncodeHintType.QR_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hints[EncodeHintType.QR_VERSION] = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
|
||||
internal class ErrorLevelConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(ErrorCorrectionLevel))
|
||||
return true;
|
||||
if (sourceType == typeof(String))
|
||||
return true;
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(ErrorCorrectionLevel))
|
||||
return true;
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
var level = value as ErrorCorrectionLevel;
|
||||
if (level != null)
|
||||
{
|
||||
return level.Name;
|
||||
}
|
||||
if (value is String)
|
||||
{
|
||||
switch (value.ToString())
|
||||
{
|
||||
case "L":
|
||||
return ErrorCorrectionLevel.L;
|
||||
case "M":
|
||||
return ErrorCorrectionLevel.M;
|
||||
case "Q":
|
||||
return ErrorCorrectionLevel.Q;
|
||||
case "H":
|
||||
return ErrorCorrectionLevel.H;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
var level = value as ErrorCorrectionLevel;
|
||||
if (level != null)
|
||||
{
|
||||
return level.Name;
|
||||
}
|
||||
if (destinationType == typeof(ErrorCorrectionLevel))
|
||||
{
|
||||
switch (value.ToString())
|
||||
{
|
||||
case "L":
|
||||
return ErrorCorrectionLevel.L;
|
||||
case "M":
|
||||
return ErrorCorrectionLevel.M;
|
||||
case "Q":
|
||||
return ErrorCorrectionLevel.Q;
|
||||
case "H":
|
||||
return ErrorCorrectionLevel.H;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
||||
{
|
||||
return new StandardValuesCollection(new[] { ErrorCorrectionLevel.L, ErrorCorrectionLevel.M, ErrorCorrectionLevel.Q, ErrorCorrectionLevel.H });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user