mirror of
https://github.com/zxing/zxing.git
synced 2024-11-10 04:54:04 -08:00
Support mirrored QR codes
git-svn-id: https://zxing.googlecode.com/svn/trunk@2815 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
11dbdde1c5
commit
b0ee984ebe
|
@ -29,6 +29,7 @@ import com.google.zxing.common.DetectorResult;
|
|||
import com.google.zxing.multi.MultipleBarcodeReader;
|
||||
import com.google.zxing.multi.qrcode.detector.MultiDetector;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -57,6 +58,10 @@ public final class QRCodeMultiReader extends QRCodeReader implements MultipleBar
|
|||
try {
|
||||
DecoderResult decoderResult = getDecoder().decode(detectorResult.getBits(), hints);
|
||||
ResultPoint[] points = detectorResult.getPoints();
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
|
||||
((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
|
||||
}
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,
|
||||
BarcodeFormat.QR_CODE);
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.google.zxing.common.BitMatrix;
|
|||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
import com.google.zxing.qrcode.decoder.Decoder;
|
||||
import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
|
||||
import com.google.zxing.qrcode.detector.Detector;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -78,6 +79,11 @@ public class QRCodeReader implements Reader {
|
|||
points = detectorResult.getPoints();
|
||||
}
|
||||
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
|
||||
((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
|
||||
}
|
||||
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
|
|
|
@ -27,6 +27,7 @@ final class BitMatrixParser {
|
|||
private final BitMatrix bitMatrix;
|
||||
private Version parsedVersion;
|
||||
private FormatInformation parsedFormatInfo;
|
||||
private boolean mirror;
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
|
@ -137,12 +138,13 @@ final class BitMatrixParser {
|
|||
}
|
||||
|
||||
private int copyBit(int i, int j, int versionBits) {
|
||||
return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||
boolean bit = mirror ? bitMatrix.get(j, i) : bitMatrix.get(i, j);
|
||||
return bit ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstitute the codewords bytes contained within the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
|
@ -155,7 +157,7 @@ final class BitMatrixParser {
|
|||
|
||||
// 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.
|
||||
DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
|
||||
DataMask dataMask = DataMask.forReference(formatInfo.getDataMask());
|
||||
int dimension = bitMatrix.getHeight();
|
||||
dataMask.unmaskBitMatrix(bitMatrix, dimension);
|
||||
|
||||
|
@ -202,4 +204,42 @@ final class BitMatrixParser {
|
|||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
void remask() {
|
||||
if (parsedFormatInfo == null) {
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
DataMask dataMask = DataMask.forReference(parsedFormatInfo.getDataMask());
|
||||
int dimension = bitMatrix.getHeight();
|
||||
dataMask.unmaskBitMatrix(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.
|
||||
*/
|
||||
void setMirror(boolean mirror) {
|
||||
parsedVersion = null;
|
||||
parsedFormatInfo = null;
|
||||
this.mirror = mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
void mirror() {
|
||||
for (int x = 0; x < bitMatrix.getWidth(); x++) {
|
||||
for (int y = x + 1; y < bitMatrix.getHeight(); y++) {
|
||||
if (bitMatrix.get(x, y) != bitMatrix.get(y, x)) {
|
||||
bitMatrix.flip(y, x);
|
||||
bitMatrix.flip(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -85,6 +85,71 @@ public final class Decoder {
|
|||
|
||||
// Construct a parser and read version, error-correction level
|
||||
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||
FormatException fe = null;
|
||||
ChecksumException ce = null;
|
||||
try {
|
||||
return decode(parser, hints);
|
||||
} catch (FormatException e) {
|
||||
fe = e;
|
||||
} catch (ChecksumException e) {
|
||||
ce = e;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// 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.
|
||||
parser.readVersion();
|
||||
|
||||
// Preemptively read the format information.
|
||||
parser.readFormatInformation();
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
DecoderResult result = decode(parser, hints);
|
||||
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
result.setOther(new QRCodeDecoderMetaData(true));
|
||||
|
||||
return result;
|
||||
|
||||
} catch (FormatException e) {
|
||||
// Throw the exception from the original reading
|
||||
if (fe != null) {
|
||||
throw fe;
|
||||
}
|
||||
if (ce != null) {
|
||||
throw ce;
|
||||
}
|
||||
throw e;
|
||||
|
||||
} catch (ChecksumException e) {
|
||||
// Throw the exception from the original reading
|
||||
if (fe != null) {
|
||||
throw fe;
|
||||
}
|
||||
if (ce != null) {
|
||||
throw ce;
|
||||
}
|
||||
throw e;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)
|
||||
throws FormatException, ChecksumException {
|
||||
Version version = parser.readVersion();
|
||||
ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see com.google.zxing.common.DecoderResult#getOther()
|
||||
*/
|
||||
public final class QRCodeDecoderMetaData {
|
||||
|
||||
private final boolean mirrored;
|
||||
|
||||
QRCodeDecoderMetaData(boolean mirrored) {
|
||||
this.mirrored = mirrored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the QR Code was mirrored.
|
||||
*/
|
||||
public boolean isMirrored() {
|
||||
return mirrored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the result points' order correction due to mirroring.
|
||||
*
|
||||
* @param points Array of points to apply mirror correction to.
|
||||
*/
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue