mirror of
https://github.com/zxing/zxing.git
synced 2024-11-13 14:34:08 -08:00
git-svn-id: https://zxing.googlecode.com/svn/trunk@6 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
43eb621f7f
commit
7726e8fcb2
29
core-ext/build.xml
Normal file
29
core-ext/build.xml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="core-ext" default="build">
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<tstamp/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="init">
|
||||||
|
<mkdir dir="build"/>
|
||||||
|
<javac srcdir="src"
|
||||||
|
destdir="build"
|
||||||
|
source="1.5"
|
||||||
|
target="1.5"
|
||||||
|
optimize="true"
|
||||||
|
debug="true"
|
||||||
|
deprecation="true">
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="../core/core.jar"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
<jar jarfile="core-ext.jar" basedir="build"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="build"/>
|
||||||
|
<delete file="core-ext.jar"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See
|
||||||
|
* <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
|
||||||
|
* DoCoMo's documentation</a> about the result types represented by subclasses of this class.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
abstract class AbstractDoCoMoResult extends ParsedReaderResult {
|
||||||
|
|
||||||
|
AbstractDoCoMoResult(ParsedReaderResultType type) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This could as well be implemented with java.util.regex. It was already implemented partially
|
||||||
|
// to run in a J2ME enviroment, where this unavailable.
|
||||||
|
|
||||||
|
static String[] matchPrefixedField(String prefix, String rawText) {
|
||||||
|
List<String> matches = null;
|
||||||
|
int i = 0;
|
||||||
|
int max = rawText.length();
|
||||||
|
while (i < max) {
|
||||||
|
i = rawText.indexOf(prefix, i);
|
||||||
|
if (i < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += prefix.length(); // Skip past this prefix we found to start
|
||||||
|
int start = i; // Found the start of a match here
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
i = rawText.indexOf((int) ';', i);
|
||||||
|
if (i < 0) {
|
||||||
|
// No terminating semicolon? uh, done. Set i such that loop terminates and break
|
||||||
|
i = rawText.length();
|
||||||
|
done = true;
|
||||||
|
} else if (rawText.charAt(i-1) == '\\') {
|
||||||
|
// semicolon was escaped so continue
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// found a match
|
||||||
|
if (matches == null) {
|
||||||
|
matches = new ArrayList<String>(3); // lazy init
|
||||||
|
}
|
||||||
|
matches.add(unescape(rawText.substring(start, i)));
|
||||||
|
i++;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return matches.toArray(new String[matches.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchSinglePrefixedField(String prefix, String rawText) {
|
||||||
|
String[] matches = matchPrefixedField(prefix, rawText);
|
||||||
|
return matches == null ? null : matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] matchRequiredPrefixedField(String prefix, String rawText) {
|
||||||
|
String[] result = matchPrefixedField(prefix, rawText);
|
||||||
|
if (result == null) {
|
||||||
|
throw new IllegalArgumentException("Did not match prefix " + prefix);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unescape(String escaped) {
|
||||||
|
if (escaped != null) {
|
||||||
|
int backslash = escaped.indexOf((int) '\\');
|
||||||
|
if (backslash >= 0) {
|
||||||
|
int max = escaped.length();
|
||||||
|
StringBuilder unescaped = new StringBuilder(max - 1);
|
||||||
|
unescaped.append(escaped.toCharArray(), 0, backslash);
|
||||||
|
boolean nextIsEscaped = false;
|
||||||
|
for (int i = backslash; i < max; i++) {
|
||||||
|
char c = escaped.charAt(i);
|
||||||
|
if (nextIsEscaped || c != '\\') {
|
||||||
|
unescaped.append(c);
|
||||||
|
nextIsEscaped = false;
|
||||||
|
} else {
|
||||||
|
nextIsEscaped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unescaped.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybeAppend(String value, StringBuilder result) {
|
||||||
|
if (value != null) {
|
||||||
|
result.append('\n');
|
||||||
|
result.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "MECARD" address book entry format.
|
||||||
|
*
|
||||||
|
* Supported keys: N, TEL, EMAIL, NOTE, ADR Unsupported keys: SOUND, TEL-AV, BDAY, URL, NICKNAME
|
||||||
|
*
|
||||||
|
* Except for TEL, multiple values for keys are also not supported;
|
||||||
|
* the first one found takes precedence.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class AddressBookDoCoMoResult extends AbstractDoCoMoResult {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String[] phoneNumbers;
|
||||||
|
private final String email;
|
||||||
|
private final String note;
|
||||||
|
private final String address;
|
||||||
|
|
||||||
|
public AddressBookDoCoMoResult(String rawText) {
|
||||||
|
super(ParsedReaderResultType.ADDRESSBOOK);
|
||||||
|
if (!rawText.startsWith("MECARD:")) {
|
||||||
|
throw new IllegalArgumentException("Does not begin with MECARD");
|
||||||
|
}
|
||||||
|
name = parseName(matchRequiredPrefixedField("N:", rawText)[0]);
|
||||||
|
phoneNumbers = matchPrefixedField("TEL:", rawText);
|
||||||
|
email = matchSinglePrefixedField("EMAIL:", rawText);
|
||||||
|
note = matchSinglePrefixedField("NOTE:", rawText);
|
||||||
|
address = matchSinglePrefixedField("ADR:", rawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPhoneNumbers() {
|
||||||
|
return phoneNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNote() {
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuilder result = new StringBuilder(name);
|
||||||
|
maybeAppend(email, result);
|
||||||
|
maybeAppend(address, result);
|
||||||
|
for (int i = 0; i < phoneNumbers.length; i++) {
|
||||||
|
maybeAppend(phoneNumbers[i], result);
|
||||||
|
}
|
||||||
|
maybeAppend(note, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseName(String name) {
|
||||||
|
int comma = name.indexOf((int) ',');
|
||||||
|
if (comma >= 0) {
|
||||||
|
// Format may be last,first; switch it around
|
||||||
|
return name.substring(comma + 1) + ' ' + name.substring(0, comma);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BookmarkDoCoMoResult extends AbstractDoCoMoResult {
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final URI uri;
|
||||||
|
|
||||||
|
public BookmarkDoCoMoResult(String rawText) {
|
||||||
|
super(ParsedReaderResultType.BOOKMARK);
|
||||||
|
if (!rawText.startsWith("MEBKM:")) {
|
||||||
|
throw new IllegalArgumentException("Does not begin with MEBKM");
|
||||||
|
}
|
||||||
|
title = matchSinglePrefixedField("TITLE:", rawText);
|
||||||
|
String uriString = matchRequiredPrefixedField("URL:", rawText)[0];
|
||||||
|
try {
|
||||||
|
this.uri = new URI(uriString);
|
||||||
|
} catch (URISyntaxException urise) {
|
||||||
|
throw new IllegalArgumentException(urise.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getURI() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
if (title == null) {
|
||||||
|
return uri.toString();
|
||||||
|
} else {
|
||||||
|
return title + '\n' + uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "MATMSG" email message entry format.
|
||||||
|
*
|
||||||
|
* Supported keys: TO, SUB, BODY
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class EmailDoCoMoResult extends AbstractDoCoMoResult {
|
||||||
|
|
||||||
|
private final String to;
|
||||||
|
private final String subject;
|
||||||
|
private final String body;
|
||||||
|
private final URI mailtoURI;
|
||||||
|
|
||||||
|
public EmailDoCoMoResult(String rawText) {
|
||||||
|
super(ParsedReaderResultType.EMAIL);
|
||||||
|
if (!rawText.startsWith("MATMSG:")) {
|
||||||
|
throw new IllegalArgumentException("Does not begin with MATMSG");
|
||||||
|
}
|
||||||
|
to = matchRequiredPrefixedField("TO:", rawText)[0];
|
||||||
|
try {
|
||||||
|
mailtoURI = new URI("mailto:" + to);
|
||||||
|
} catch (URISyntaxException urise) {
|
||||||
|
throw new IllegalArgumentException(urise.toString());
|
||||||
|
}
|
||||||
|
subject = matchSinglePrefixedField("SUB:", rawText);
|
||||||
|
body = matchSinglePrefixedField("BODY:", rawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTo() {
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getMailtoURI() {
|
||||||
|
return mailtoURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuilder result = new StringBuilder(to);
|
||||||
|
maybeAppend(subject, result);
|
||||||
|
maybeAppend(body, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public abstract class ParsedReaderResult {
|
||||||
|
|
||||||
|
private final ParsedReaderResultType type;
|
||||||
|
|
||||||
|
ParsedReaderResult(ParsedReaderResultType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParsedReaderResultType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getDisplayResult();
|
||||||
|
|
||||||
|
public static ParsedReaderResult parseReaderResult(String rawText) {
|
||||||
|
for (ParsedReaderResultType type : ParsedReaderResultType.values()) {
|
||||||
|
Class<? extends ParsedReaderResult> resultClass = type.getResultClass();
|
||||||
|
try {
|
||||||
|
Constructor<? extends ParsedReaderResult> constructor =
|
||||||
|
resultClass.getConstructor(String.class);
|
||||||
|
return constructor.newInstance(rawText);
|
||||||
|
} catch (InvocationTargetException ite) {
|
||||||
|
Throwable cause = ite.getCause();
|
||||||
|
if (cause instanceof IllegalArgumentException) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(cause);
|
||||||
|
} catch (NoSuchMethodException nsme) {
|
||||||
|
throw new RuntimeException(nsme);
|
||||||
|
} catch (IllegalAccessException iae) {
|
||||||
|
throw new RuntimeException(iae);
|
||||||
|
} catch (InstantiationException ie) {
|
||||||
|
throw new RuntimeException(ie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("TextResult should always work");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getDisplayResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public enum ParsedReaderResultType {
|
||||||
|
|
||||||
|
// Order is important: if a string could be construed as multiple types,
|
||||||
|
// put the most specific one first
|
||||||
|
BOOKMARK(BookmarkDoCoMoResult.class),
|
||||||
|
ADDRESSBOOK(AddressBookDoCoMoResult.class),
|
||||||
|
EMAIL(EmailDoCoMoResult.class),
|
||||||
|
URI(URIParsedResult.class),
|
||||||
|
TEXT(TextParsedResult.class);
|
||||||
|
|
||||||
|
private Class<? extends ParsedReaderResult> resultClass;
|
||||||
|
|
||||||
|
ParsedReaderResultType(Class<? extends ParsedReaderResult> resultClass) {
|
||||||
|
this.resultClass = resultClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<? extends ParsedReaderResult> getResultClass() {
|
||||||
|
return resultClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class TextParsedResult extends ParsedReaderResult {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public TextParsedResult(String rawText) {
|
||||||
|
super(ParsedReaderResultType.TEXT);
|
||||||
|
text = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.result;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class URIParsedResult extends ParsedReaderResult {
|
||||||
|
|
||||||
|
private final URI uri;
|
||||||
|
|
||||||
|
public URIParsedResult(String rawText) {
|
||||||
|
super(ParsedReaderResultType.URI);
|
||||||
|
try {
|
||||||
|
uri = new URI(rawText);
|
||||||
|
} catch (URISyntaxException urise) {
|
||||||
|
throw new IllegalArgumentException(urise.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getURI() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return uri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
core/build.xml
Normal file
30
core/build.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="core" default="build">
|
||||||
|
|
||||||
|
<property name="WTK-home" value="/usr/local/WTK2.5.2"/>
|
||||||
|
<property name="JDK1.4-home" value="/usr/lib/jvm/j2sdk1.4.2_16"/>
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<tstamp/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="init">
|
||||||
|
<mkdir dir="build"/>
|
||||||
|
<javac srcdir="src"
|
||||||
|
destdir="build"
|
||||||
|
source="1.4"
|
||||||
|
target="1.4"
|
||||||
|
bootclasspath="${JDK1.4-home}/jre/lib/rt.jar"
|
||||||
|
optimize="true"
|
||||||
|
debug="true"
|
||||||
|
deprecation="true"
|
||||||
|
fork="true"/>
|
||||||
|
<jar jarfile="core.jar" basedir="build"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="build"/>
|
||||||
|
<delete file="core.jar"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
32
core/src/com/google/zxing/BarcodeFormat.java
Normal file
32
core/src/com/google/zxing/BarcodeFormat.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates barcode formats known to this package.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BarcodeFormat {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
public static final BarcodeFormat UPC = new BarcodeFormat();
|
||||||
|
public static final BarcodeFormat QR_CODE = new BarcodeFormat();
|
||||||
|
public static final BarcodeFormat DATAMATRIX = new BarcodeFormat();
|
||||||
|
|
||||||
|
}
|
43
core/src/com/google/zxing/DecodeHintType.java
Normal file
43
core/src/com/google/zxing/DecodeHintType.java
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a type of hint that a caller may pass to a barcode reader to help it
|
||||||
|
* more quickly or accurately decode it. It is up to implementations to decide what,
|
||||||
|
* if anything, to do with the information that is supplied.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen), dswitkin@google.com (Daniel Switkin)
|
||||||
|
* @see Reader#decode(MonochromeBitmapSource, java.util.Map)
|
||||||
|
*/
|
||||||
|
public final class DecodeHintType {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
/** Unspecified, application-specific hint. */
|
||||||
|
public static final DecodeHintType OTHER = new DecodeHintType();
|
||||||
|
/** Image is a pure monochrome image of a barcode. */
|
||||||
|
public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
|
||||||
|
/**
|
||||||
|
* Image is known to be of one of a few possible formats.
|
||||||
|
* Maps to {@link java.util.Collection} of {@link BarcodeFormat}s.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
|
||||||
|
|
||||||
|
private DecodeHintType() {}
|
||||||
|
|
||||||
|
}
|
61
core/src/com/google/zxing/MonochromeBitmapSource.java
Normal file
61
core/src/com/google/zxing/MonochromeBitmapSource.java
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a generic black-and-white bitmap -- a collection of pixels in two dimensions.
|
||||||
|
* This unifies many possible representations, like AWT's <code>BufferedImage</code>.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public interface MonochromeBitmapSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x horizontal offset, from left, of the pixel
|
||||||
|
* @param y vertical offset, from top, of the pixel
|
||||||
|
* @return true iff the pixel at (x,y) is black
|
||||||
|
*/
|
||||||
|
boolean isBlack(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an entire row of black/white pixels as an array of bits, where "true" means "black".
|
||||||
|
* This is a sort of "bulk get" operation intended to enable efficient access in
|
||||||
|
* certain situations.
|
||||||
|
*
|
||||||
|
* @param y vertical offset, from top, of the row of pixels
|
||||||
|
* @param row if not null, {@link BitArray} to write pixels into. If null, a new {@link BitArray}
|
||||||
|
* is allocated and returned.
|
||||||
|
* @param startX horizontal offset, from left, from which to start getting pixels
|
||||||
|
* @param getWidth number of pixels to get from the row
|
||||||
|
* @return {@link BitArray} representing the (subset of the) row of pixels. If row parameter
|
||||||
|
* was not null, it is returned.
|
||||||
|
*/
|
||||||
|
BitArray getBlackRow(int y, BitArray row, int startX, int getWidth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return height of underlying image
|
||||||
|
*/
|
||||||
|
int getHeight();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return width of underlying image
|
||||||
|
*/
|
||||||
|
int getWidth();
|
||||||
|
|
||||||
|
}
|
45
core/src/com/google/zxing/MultiFormatReader.java
Normal file
45
core/src/com/google/zxing/MultiFormatReader.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.google.zxing.qrcode.QRCodeReader;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For now, only delegates to {@link QRCodeReader}.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen), dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class MultiFormatReader implements Reader {
|
||||||
|
|
||||||
|
public Result decode(MonochromeBitmapSource image) throws ReaderException {
|
||||||
|
return decode(image, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result decode(MonochromeBitmapSource image, Hashtable hints)
|
||||||
|
throws ReaderException {
|
||||||
|
Hashtable possibleFormats =
|
||||||
|
hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS);
|
||||||
|
if (possibleFormats == null || possibleFormats.contains(BarcodeFormat.QR_CODE)) {
|
||||||
|
return new QRCodeReader().decode(image, hints);
|
||||||
|
} else {
|
||||||
|
throw new ReaderException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
core/src/com/google/zxing/Reader.java
Normal file
56
core/src/com/google/zxing/Reader.java
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this interface can decode an image of a barcode in some format into
|
||||||
|
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
|
||||||
|
* decode a QR code. The decoder may optionally receive hints from the caller which may help
|
||||||
|
* it decode more quickly or accurately.
|
||||||
|
*
|
||||||
|
* See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
|
||||||
|
* format is present within the image as well, and then decodes it accordingly.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen), dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public interface Reader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a barcode in some format within an image.
|
||||||
|
*
|
||||||
|
* @param image image of barcode to decode
|
||||||
|
* @return String which the barcode encodes
|
||||||
|
* @throws ReaderException if the barcode cannot be located or decoded for any reason
|
||||||
|
*/
|
||||||
|
Result decode(MonochromeBitmapSource image) throws ReaderException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param image image of barcode to decode
|
||||||
|
* @param hints passed as a {@link Hashtable} from {@link DecodeHintType} to aribtrary data. The
|
||||||
|
* meaning of the data depends upon the hint type. The implementation may or may not do
|
||||||
|
* anything with these hints.
|
||||||
|
* @return String which the barcode encodes
|
||||||
|
* @throws ReaderException if the barcode cannot be located or decoded for any reason
|
||||||
|
*/
|
||||||
|
Result decode(MonochromeBitmapSource image, Hashtable hints) throws ReaderException;
|
||||||
|
|
||||||
|
}
|
35
core/src/com/google/zxing/ReaderException.java
Normal file
35
core/src/com/google/zxing/ReaderException.java
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general exception class throw when something goes wrong during decoding of a barcode.
|
||||||
|
* This includes, but is not limited to, failing checksums / error correction algorithms, being
|
||||||
|
* unable to locate finder timing patterns, and so on.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class ReaderException extends Exception {
|
||||||
|
|
||||||
|
public ReaderException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReaderException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
core/src/com/google/zxing/Result.java
Normal file
50
core/src/com/google/zxing/Result.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of decoding a barcode within an image.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class Result {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
private final ResultPoint[] resultPoints;
|
||||||
|
|
||||||
|
public Result(String text, ResultPoint[] resultPoints) {
|
||||||
|
this.text = text;
|
||||||
|
this.resultPoints = resultPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return raw text encoded by the barcode, if any
|
||||||
|
*/
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return points related to the barcode in the image. These are typically points
|
||||||
|
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||||
|
* specific to the type of barcode that was decoded.
|
||||||
|
*/
|
||||||
|
public ResultPoint[] getResultPoints() {
|
||||||
|
return resultPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
core/src/com/google/zxing/ResultPoint.java
Normal file
30
core/src/com/google/zxing/ResultPoint.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
|
||||||
|
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public interface ResultPoint {
|
||||||
|
|
||||||
|
float getX();
|
||||||
|
float getY();
|
||||||
|
|
||||||
|
}
|
72
core/src/com/google/zxing/common/BitArray.java
Normal file
72
core/src/com/google/zxing/common/BitArray.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BitArray {
|
||||||
|
|
||||||
|
private final int[] bits;
|
||||||
|
|
||||||
|
public BitArray(int size) {
|
||||||
|
int arraySize = size >> 5;
|
||||||
|
if ((size & 0x1F) != 0) {
|
||||||
|
arraySize++;
|
||||||
|
}
|
||||||
|
bits = new int[arraySize];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true iff bit i is set
|
||||||
|
*/
|
||||||
|
public boolean get(int i) {
|
||||||
|
return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets bit i.
|
||||||
|
*/
|
||||||
|
public void set(int i) {
|
||||||
|
bits[i >> 5] |= 1 << (i & 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBulk(int i, int newBits) {
|
||||||
|
bits[i >> 5] = newBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all bits.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
int max = bits.length;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
bits[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return underlying array of ints. The first element holds the first 32 bits, and the least
|
||||||
|
* significant bit is bit 0.
|
||||||
|
*/
|
||||||
|
public int[] getBitArray() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
core/src/com/google/zxing/common/BitMatrix.java
Executable file
106
core/src/com/google/zxing/common/BitMatrix.java
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represnts a square matrix of bits. In function arguments below, i is the row position
|
||||||
|
* and j the column position of a bit. The top left bit corresponds to i = 0 and j = 0.</p>
|
||||||
|
*
|
||||||
|
* <p>Internally the bits are represented in a compact 1-D array of 32-bit ints. The
|
||||||
|
* ordering of bits is column-major; that is the bits in this array correspond to
|
||||||
|
* j=0 and i=0..dimension-1 first, then j=1 and i=0..dimension-1, etc.</p>
|
||||||
|
*
|
||||||
|
* <p>Within each int, less-signficant bits correspond to lower values of i and higher rows.
|
||||||
|
* That is, the top-left bit is the least significant bit of the first int.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is a convenient wrapper around this representation, but also exposes the internal
|
||||||
|
* array for efficient access and manipulation.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BitMatrix {
|
||||||
|
|
||||||
|
private final int dimension;
|
||||||
|
private final int[] bits;
|
||||||
|
|
||||||
|
public BitMatrix(int dimension) {
|
||||||
|
if (dimension < 1) {
|
||||||
|
throw new IllegalArgumentException("dimension must be at least 1");
|
||||||
|
}
|
||||||
|
this.dimension = dimension;
|
||||||
|
int numBits = dimension * dimension;
|
||||||
|
int arraySize = numBits >> 5; // one int per 32 bits
|
||||||
|
if ((numBits & 0x1F) != 0) { // plus one more if there are leftovers
|
||||||
|
arraySize++;
|
||||||
|
}
|
||||||
|
bits = new int[arraySize];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean get(int i, int j) {
|
||||||
|
int offset = i + dimension * j;
|
||||||
|
return ((bits[offset >> 5] >>> (offset & 0x1F)) & 0x01) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int i, int j) {
|
||||||
|
int offset = i + dimension * j;
|
||||||
|
bits[offset >> 5] |= 1 << (offset & 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegion(int topI, int leftJ, int height, int width) {
|
||||||
|
if (topI < 0 || leftJ < 0) {
|
||||||
|
throw new IllegalArgumentException("topI and leftJ must be nonnegative");
|
||||||
|
}
|
||||||
|
if (height < 1 || width < 1) {
|
||||||
|
throw new IllegalArgumentException("height and width must be at least 1");
|
||||||
|
}
|
||||||
|
int maxJ = leftJ + width;
|
||||||
|
int maxI = topI + height;
|
||||||
|
if (maxI > dimension || maxJ > dimension) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"topI + height and leftJ + width must be <= matrix dimension");
|
||||||
|
}
|
||||||
|
for (int j = leftJ; j < maxJ; j++) {
|
||||||
|
int jOffset = dimension * j;
|
||||||
|
for (int i = topI; i < maxI; i++) {
|
||||||
|
int offset = i + jOffset;
|
||||||
|
bits[offset >> 5] |= 1 << (offset & 0x1F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDimension() {
|
||||||
|
return dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getBits() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public BufferedImage toBufferedImage() {
|
||||||
|
BufferedImage image =
|
||||||
|
new BufferedImage(dimension, dimension, BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
image.setRGB(j, i, get(i, j) ? 0x00000000 : 0x00FFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
93
core/src/com/google/zxing/common/BlackPointEstimator.java
Normal file
93
core/src/com/google/zxing/common/BlackPointEstimator.java
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates logic that estimates the optimal "black point", the luminance value
|
||||||
|
* which is the best line between "white" and "black" in a grayscale image.</p>
|
||||||
|
*
|
||||||
|
* <p>TODO: Include reference to paper with similar ideas</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BlackPointEstimator {
|
||||||
|
|
||||||
|
private BlackPointEstimator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Given an array of <em>counts</em> of luminance values (i.e. a histogram), this method
|
||||||
|
* decides which bucket of values corresponds to the black point -- which bucket contains the
|
||||||
|
* count of the brightest luminance values that should be considered "black".</p>
|
||||||
|
*
|
||||||
|
* @param luminanceBuckets an array of <em>counts</em> of luminance values
|
||||||
|
* @return index within argument of bucket corresponding to brightest values which should be
|
||||||
|
* considered "black"
|
||||||
|
*/
|
||||||
|
public static int estimate(int[] luminanceBuckets) {
|
||||||
|
|
||||||
|
int numBuckets = luminanceBuckets.length;
|
||||||
|
|
||||||
|
// Find tallest peak in histogram
|
||||||
|
int firstPeak = 0;
|
||||||
|
int firstPeakSize = 0;
|
||||||
|
for (int i = 0; i < numBuckets; i++) {
|
||||||
|
if (luminanceBuckets[i] > firstPeakSize) {
|
||||||
|
firstPeak = i;
|
||||||
|
firstPeakSize = luminanceBuckets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find second-tallest peak -- well, another peak that is tall and not
|
||||||
|
// so close to the first one
|
||||||
|
int secondPeak = 0;
|
||||||
|
int secondPeakScore = 0;
|
||||||
|
for (int i = 0; i < numBuckets; i++) {
|
||||||
|
int distanceToBiggest = i - firstPeak;
|
||||||
|
// Encourage more distant second peaks by multiplying by square
|
||||||
|
// of distance
|
||||||
|
int score = luminanceBuckets[i] * distanceToBiggest * distanceToBiggest;
|
||||||
|
if (score > secondPeakScore) {
|
||||||
|
secondPeak = i;
|
||||||
|
secondPeakScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put firstPeak first
|
||||||
|
if (firstPeak > secondPeak) {
|
||||||
|
int temp = firstPeak;
|
||||||
|
firstPeak = secondPeak;
|
||||||
|
secondPeak = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a valley between them that is low and close to the midpoint of the two peaks
|
||||||
|
int bestValley = firstPeak;
|
||||||
|
int bestValleyScore = 0;
|
||||||
|
for (int i = firstPeak + 1; i < secondPeak; i++) {
|
||||||
|
// Encourage low valleys near the mid point between peaks
|
||||||
|
int score = (firstPeakSize - luminanceBuckets[i]) *
|
||||||
|
(i - firstPeak) * (secondPeak - i);
|
||||||
|
if (score > bestValleyScore) {
|
||||||
|
bestValley = i;
|
||||||
|
bestValleyScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestValley;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
core/src/com/google/zxing/common/Collections.java
Normal file
50
core/src/com/google/zxing/common/Collections.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is basically a substitute for <code>java.util.Collections</code>.
|
||||||
|
*/
|
||||||
|
public final class Collections {
|
||||||
|
|
||||||
|
private Collections() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts its argument (destructively) using insert sort; in the context of this package
|
||||||
|
* insertion sort is simple and efficient given its relatively small inputs.
|
||||||
|
*
|
||||||
|
* @param vector
|
||||||
|
* @param comparator
|
||||||
|
*/
|
||||||
|
public static void insertionSort(Vector vector, Comparator comparator) {
|
||||||
|
int max = vector.size();
|
||||||
|
for (int i = 1; i < max; i++) {
|
||||||
|
Object value = vector.elementAt(i);
|
||||||
|
int j = i - 1;
|
||||||
|
Object valueB;
|
||||||
|
while (j >= 0 && comparator.compare((valueB = vector.elementAt(j)), value) > 0) {
|
||||||
|
vector.setElementAt(valueB, j + 1);
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
vector.setElementAt(value, j + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
core/src/com/google/zxing/common/Comparator.java
Normal file
27
core/src/com/google/zxing/common/Comparator.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is merely a clone of <code>Comparator</code> since it is not available in
|
||||||
|
* CLDC 1.1 / MIDP 2.0.
|
||||||
|
*/
|
||||||
|
public interface Comparator {
|
||||||
|
|
||||||
|
int compare(Object o1, Object o2);
|
||||||
|
|
||||||
|
}
|
94
core/src/com/google/zxing/common/reedsolomon/GF256.java
Normal file
94
core/src/com/google/zxing/common/reedsolomon/GF256.java
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class contains utility methods for performing mathematical operations over
|
||||||
|
* the Galois Field GF(256). Operations use the primitive polynomial
|
||||||
|
* x^8 + x^4 + x^3 + x^2 + 1 in calculations.</p>
|
||||||
|
*
|
||||||
|
* <p>Throughout this package, elements of GF(256) are represented as an <code>int</code>
|
||||||
|
* for convenience and speed (but at the cost of memory).
|
||||||
|
* Only the bottom 8 bits are really used.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class GF256 {
|
||||||
|
|
||||||
|
private static final int PRIMITIVE = 0x011D;
|
||||||
|
private static final int[] exp = new int[256];
|
||||||
|
private static final int[] log = new int[256];
|
||||||
|
static {
|
||||||
|
int x = 1;
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
exp[i] = x;
|
||||||
|
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
|
||||||
|
if (x >= 0x100) {
|
||||||
|
x ^= PRIMITIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 255; i++) {
|
||||||
|
log[exp[i]] = i;
|
||||||
|
}
|
||||||
|
// log[0] == 0 but this should never be used
|
||||||
|
}
|
||||||
|
|
||||||
|
private GF256() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addition and subtraction are the same in GF(256).
|
||||||
|
*/
|
||||||
|
static int addOrSubtract(int a, int b) {
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exp(int a) {
|
||||||
|
return exp[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return log[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return multiplicative inverse of a
|
||||||
|
*/
|
||||||
|
static int inverse(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
return exp[255 - log[a]];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int multiply(int a, int b) {
|
||||||
|
if (a == 0 || b == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a == 1) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b == 1) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return exp[(log[a] + log[b]) % 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
224
core/src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal file
224
core/src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represents a polynomial whose coefficients are elements of GF(256).
|
||||||
|
* Instances of this class are immutable.</p>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class GF256Poly {
|
||||||
|
|
||||||
|
/** Polynimal representing the monomial 0. */
|
||||||
|
static final GF256Poly ZERO = new GF256Poly(new int[] { 0 });
|
||||||
|
/** Polynimal representing the monomial 1. */
|
||||||
|
static final GF256Poly ONE = new GF256Poly(new int[] { 1 });
|
||||||
|
|
||||||
|
private final int[] coefficients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param coefficients coefficients as ints representing elements of GF(256), arranged
|
||||||
|
* from most significant (highest-power term) coefficient to least significant
|
||||||
|
* @throws IllegalArgumentException if argument is null or empty,
|
||||||
|
* or if leading coefficient is 0 and this is not a
|
||||||
|
* constant polynomial (that is, it is not the monomial "0")
|
||||||
|
*/
|
||||||
|
GF256Poly(int[] coefficients) {
|
||||||
|
if (coefficients == null || coefficients.length == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (coefficients.length > 1 && coefficients[0] == 0) {
|
||||||
|
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||||
|
int firstNonZero = 1;
|
||||||
|
while (firstNonZero < coefficients.length && coefficients[firstNonZero] == 0) {
|
||||||
|
firstNonZero++;
|
||||||
|
}
|
||||||
|
if (firstNonZero == coefficients.length) {
|
||||||
|
this.coefficients = ZERO.coefficients;
|
||||||
|
} else {
|
||||||
|
this.coefficients = new int[coefficients.length - firstNonZero];
|
||||||
|
System.arraycopy(coefficients,
|
||||||
|
firstNonZero,
|
||||||
|
this.coefficients,
|
||||||
|
0,
|
||||||
|
this.coefficients.length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.coefficients = coefficients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return degree of this polynomial
|
||||||
|
*/
|
||||||
|
int getDegree() {
|
||||||
|
return coefficients.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true iff this polynomial is the monomial "0"
|
||||||
|
*/
|
||||||
|
boolean isZero() {
|
||||||
|
return coefficients[0] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the monomial representing coefficient * x^degree
|
||||||
|
*/
|
||||||
|
static GF256Poly buildMonomial(int degree, int coefficient) {
|
||||||
|
if (degree < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (coefficient == 0) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
int[] coefficients = new int[degree + 1];
|
||||||
|
coefficients[0] = coefficient;
|
||||||
|
return new GF256Poly(coefficients);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return coefficient of x^degree term in this polynomial
|
||||||
|
*/
|
||||||
|
int getCoefficient(int degree) {
|
||||||
|
return coefficients[coefficients.length - 1 - degree];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return evaluation of this polynomial at a given point
|
||||||
|
*/
|
||||||
|
int evaluateAt(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
// Just return the x^0 coefficient
|
||||||
|
return getCoefficient(0);
|
||||||
|
}
|
||||||
|
final int size = coefficients.length;
|
||||||
|
if (a == 1) {
|
||||||
|
// Just the sum of the coefficients
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
result = GF256.addOrSubtract(result, coefficients[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int result = coefficients[0];
|
||||||
|
for (int i = 1; i < size; i++) {
|
||||||
|
result = GF256.addOrSubtract(GF256.multiply(a, result), coefficients[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int evaluateFormatDerivativeAt(int a) {
|
||||||
|
int degree = getDegree();
|
||||||
|
if (degree == 0) {
|
||||||
|
// Derivative of a constant is zero.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aToTheI = 1;
|
||||||
|
int sum = getCoefficient(1);
|
||||||
|
int aSquared = GF256.multiply(a, a);
|
||||||
|
for (int i = 2; i < degree; i += 2) {
|
||||||
|
aToTheI = GF256.multiply(aSquared, aToTheI);
|
||||||
|
sum = GF256.addOrSubtract(sum, GF256.multiply(aToTheI, getCoefficient(i + 1)));
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly addOrSubtract(GF256Poly other) {
|
||||||
|
if (isZero()) {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
if (other.isZero()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] smallerCoefficients = this.coefficients;
|
||||||
|
int[] largerCoefficients = other.coefficients;
|
||||||
|
if (smallerCoefficients.length > largerCoefficients.length) {
|
||||||
|
int[] temp = smallerCoefficients;
|
||||||
|
smallerCoefficients = largerCoefficients;
|
||||||
|
largerCoefficients = temp;
|
||||||
|
}
|
||||||
|
int[] sumDiff = new int[largerCoefficients.length];
|
||||||
|
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
|
||||||
|
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||||
|
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
|
||||||
|
|
||||||
|
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
|
||||||
|
sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GF256Poly(sumDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiply(GF256Poly other) {
|
||||||
|
if (isZero() || other.isZero()) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
int[] aCoefficients = this.coefficients;
|
||||||
|
int aLength = aCoefficients.length;
|
||||||
|
int[] bCoefficients = other.coefficients;
|
||||||
|
int bLength = bCoefficients.length;
|
||||||
|
int[] product = new int[aLength + bLength - 1];
|
||||||
|
for (int i = 0; i < aLength; i++) {
|
||||||
|
int aCoeff = aCoefficients[i];
|
||||||
|
for (int j = 0; j < bLength; j++) {
|
||||||
|
product[i + j] = GF256.addOrSubtract(product[i + j],
|
||||||
|
GF256.multiply(aCoeff, bCoefficients[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new GF256Poly(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiply(int scalar) {
|
||||||
|
if (scalar == 0) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
if (scalar == 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
int size = coefficients.length;
|
||||||
|
int[] product = new int[size];
|
||||||
|
System.arraycopy(coefficients, 0, product, 0, size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
product[i] = GF256.multiply(product[i], scalar);
|
||||||
|
}
|
||||||
|
return new GF256Poly(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiplyByMonomial(int degree, int coefficient) {
|
||||||
|
if (degree < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (coefficient == 0) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
int size = coefficients.length;
|
||||||
|
int[] product = new int[size + degree];
|
||||||
|
System.arraycopy(coefficients, 0, product, 0, size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
product[i] = GF256.multiply(product[i], coefficient);
|
||||||
|
}
|
||||||
|
return new GF256Poly(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common.reedsolomon;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
|
||||||
|
*
|
||||||
|
* <p>The algorithm will not be explained here, but the following references were helpful
|
||||||
|
* in creating this implementation:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Bruce Maggs.
|
||||||
|
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
|
||||||
|
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
|
||||||
|
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
|
||||||
|
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
|
||||||
|
* (see discussion of Euclidean algorithm)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonDecoder {
|
||||||
|
|
||||||
|
private ReedSolomonDecoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void decode(int[] received, int twoS) throws ReedSolomonException {
|
||||||
|
GF256Poly poly = new GF256Poly(received);
|
||||||
|
int[] syndromeCoefficients = new int[twoS];
|
||||||
|
for (int i = 0; i < twoS; i++) {
|
||||||
|
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = poly.evaluateAt(GF256.exp(i));
|
||||||
|
}
|
||||||
|
GF256Poly syndrome = new GF256Poly(syndromeCoefficients);
|
||||||
|
if (!syndrome.isZero()) { // Error
|
||||||
|
GF256Poly[] sigmaOmega =
|
||||||
|
runEuclideanAlgorithm(GF256Poly.buildMonomial(twoS, 1), syndrome, twoS);
|
||||||
|
int[] errorLocations = findErrorLocations(sigmaOmega[0]);
|
||||||
|
int[] errorMagnitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations);
|
||||||
|
for (int i = 0; i < errorLocations.length; i++) {
|
||||||
|
int position = received.length - 1 - GF256.log(errorLocations[i]);
|
||||||
|
received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R)
|
||||||
|
throws ReedSolomonException {
|
||||||
|
// Assume a's degree is >= b's
|
||||||
|
if (a.getDegree() < b.getDegree()) {
|
||||||
|
GF256Poly temp = a;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly rLast = a;
|
||||||
|
GF256Poly r = b;
|
||||||
|
GF256Poly sLast = GF256Poly.ONE;
|
||||||
|
GF256Poly s = GF256Poly.ZERO;
|
||||||
|
GF256Poly tLast = GF256Poly.ZERO;
|
||||||
|
GF256Poly t = GF256Poly.ONE;
|
||||||
|
|
||||||
|
// Run Euclidean algorithm until r's degree is less than R/2
|
||||||
|
while (r.getDegree() >= R / 2) {
|
||||||
|
GF256Poly rLastLast = rLast;
|
||||||
|
GF256Poly sLastLast = sLast;
|
||||||
|
GF256Poly tLastLast = tLast;
|
||||||
|
rLast = r;
|
||||||
|
sLast = s;
|
||||||
|
tLast = t;
|
||||||
|
|
||||||
|
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||||
|
if (rLast.isZero()) {
|
||||||
|
// Oops, Euclidean algorithm already terminated?
|
||||||
|
throw new ReedSolomonException("r_{i-1} was zero");
|
||||||
|
}
|
||||||
|
r = rLastLast;
|
||||||
|
GF256Poly q = GF256Poly.ZERO;
|
||||||
|
int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
|
||||||
|
int dltInverse = GF256.inverse(denominatorLeadingTerm);
|
||||||
|
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
|
||||||
|
int degreeDiff = r.getDegree() - rLast.getDegree();
|
||||||
|
int scale = GF256.multiply(r.getCoefficient(r.getDegree()), dltInverse);
|
||||||
|
q = q.addOrSubtract(GF256Poly.buildMonomial(degreeDiff, scale));
|
||||||
|
r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
s = q.multiply(sLast).addOrSubtract(sLastLast);
|
||||||
|
t = q.multiply(tLast).addOrSubtract(tLastLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigmaTildeAtZero = t.getCoefficient(0);
|
||||||
|
if (sigmaTildeAtZero == 0) {
|
||||||
|
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
int inverse = GF256.inverse(sigmaTildeAtZero);
|
||||||
|
GF256Poly sigma = t.multiply(inverse);
|
||||||
|
GF256Poly omega = r.multiply(inverse);
|
||||||
|
return new GF256Poly[] { sigma, omega };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] findErrorLocations(GF256Poly errorLocator)
|
||||||
|
throws ReedSolomonException {
|
||||||
|
// This is a direct application of Chien's search
|
||||||
|
Vector errorLocations = new Vector(3);
|
||||||
|
for (int i = 1; i < 256; i++) {
|
||||||
|
if (errorLocator.evaluateAt(i) == 0) {
|
||||||
|
errorLocations.addElement(new Integer(GF256.inverse(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorLocations.size() != errorLocator.getDegree()) {
|
||||||
|
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||||
|
}
|
||||||
|
int[] result = new int[errorLocations.size()]; // Can't use toArray() here
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = ((Integer) errorLocations.elementAt(i)).intValue();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] findErrorMagnitudes(GF256Poly errorEvaluator,
|
||||||
|
int[] errorLocations) {
|
||||||
|
// This is directly applying Forney's Formula
|
||||||
|
int s = errorLocations.length;
|
||||||
|
int[] result = new int[s];
|
||||||
|
for (int i = 0; i < errorLocations.length; i++) {
|
||||||
|
int xiInverse = GF256.inverse(errorLocations[i]);
|
||||||
|
int denominator = 1;
|
||||||
|
for (int j = 0; j < s; j++) {
|
||||||
|
if (i != j) {
|
||||||
|
denominator = GF256.multiply(denominator,
|
||||||
|
GF256.addOrSubtract(1, GF256.multiply(errorLocations[j], xiInverse)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[i] = GF256.multiply(errorEvaluator.evaluateAt(xiInverse),
|
||||||
|
GF256.inverse(denominator));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
|
||||||
|
* there are too many errors to correct.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonException extends Exception {
|
||||||
|
|
||||||
|
public ReedSolomonException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReedSolomonException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
core/src/com/google/zxing/qrcode/QRCodeReader.java
Normal file
106
core/src/com/google/zxing/qrcode/QRCodeReader.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.Reader;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.qrcode.decoder.Decoder;
|
||||||
|
import com.google.zxing.qrcode.detector.Detector;
|
||||||
|
import com.google.zxing.qrcode.detector.DetectorResult;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class QRCodeReader implements Reader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a QR code in an image.
|
||||||
|
*
|
||||||
|
* @return a String representing the content encoded by the QR code
|
||||||
|
* @throws ReaderException if a QR code cannot be found, or cannot be decoded
|
||||||
|
*/
|
||||||
|
public Result decode(MonochromeBitmapSource image) throws ReaderException {
|
||||||
|
return decode(image, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result decode(MonochromeBitmapSource image, Hashtable hints)
|
||||||
|
throws ReaderException {
|
||||||
|
String text;
|
||||||
|
ResultPoint[] points;
|
||||||
|
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
|
||||||
|
BitMatrix bits = extractPureBits(image);
|
||||||
|
text = Decoder.decode(bits);
|
||||||
|
points = new ResultPoint[0];
|
||||||
|
} else {
|
||||||
|
DetectorResult result = new Detector(image).detect();
|
||||||
|
text = Decoder.decode(result.getBits());
|
||||||
|
points = result.getPoints();
|
||||||
|
}
|
||||||
|
return new Result(text, points);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BitMatrix extractPureBits(MonochromeBitmapSource image)
|
||||||
|
throws ReaderException {
|
||||||
|
// Now need to determine module size in pixels
|
||||||
|
// First, skip white border
|
||||||
|
int borderWidth = 0;
|
||||||
|
while (!image.isBlack(borderWidth, borderWidth)) {
|
||||||
|
borderWidth++;
|
||||||
|
}
|
||||||
|
int moduleEnd = borderWidth;
|
||||||
|
while (image.isBlack(moduleEnd, moduleEnd)) {
|
||||||
|
moduleEnd++;
|
||||||
|
}
|
||||||
|
int moduleSize = moduleEnd - borderWidth;
|
||||||
|
|
||||||
|
int rowEndOfSymbol = image.getWidth() - 1;
|
||||||
|
while (!image.isBlack(rowEndOfSymbol, borderWidth)) {
|
||||||
|
rowEndOfSymbol--;
|
||||||
|
}
|
||||||
|
rowEndOfSymbol++;
|
||||||
|
|
||||||
|
if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) {
|
||||||
|
throw new ReaderException("Bad module size / width: " + moduleSize +
|
||||||
|
" / " + (rowEndOfSymbol - borderWidth));
|
||||||
|
}
|
||||||
|
int dimension = (rowEndOfSymbol - borderWidth) / moduleSize;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
borderWidth += moduleSize >> 1;
|
||||||
|
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int iOffset = borderWidth + i * moduleSize;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image.isBlack(borderWidth + j * moduleSize, iOffset)) {
|
||||||
|
bits.set(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
180
core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
Normal file
180
core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class BitMatrixParser {
|
||||||
|
|
||||||
|
private final BitMatrix bitMatrix;
|
||||||
|
private Version parsedVersion;
|
||||||
|
private FormatInformation parsedFormatInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws com.google.zxing.ReaderException
|
||||||
|
* if dimension is not >= 21 and 1 mod 4
|
||||||
|
*/
|
||||||
|
BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {
|
||||||
|
int dimension = bitMatrix.getDimension();
|
||||||
|
if (dimension < 21 || (dimension & 0x03) != 1) {
|
||||||
|
throw new ReaderException("Dimension must be 1 mod 4 and >= 21");
|
||||||
|
}
|
||||||
|
this.bitMatrix = bitMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatInformation readFormatInformation() throws ReaderException {
|
||||||
|
|
||||||
|
if (parsedFormatInfo != null) {
|
||||||
|
return parsedFormatInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read top-left format info bits
|
||||||
|
int formatInfoBits = 0;
|
||||||
|
for (int j = 0; j < 6; j++) {
|
||||||
|
formatInfoBits = copyBit(8, j, formatInfoBits);
|
||||||
|
}
|
||||||
|
// .. and skip a bit in the timing pattern ...
|
||||||
|
formatInfoBits = copyBit(8, 7, formatInfoBits);
|
||||||
|
formatInfoBits = copyBit(8, 8, formatInfoBits);
|
||||||
|
formatInfoBits = copyBit(7, 8, formatInfoBits);
|
||||||
|
// .. and skip a bit in the timing pattern ...
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
formatInfoBits = copyBit(i, 8, formatInfoBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
|
||||||
|
if (parsedFormatInfo != null) {
|
||||||
|
return parsedFormatInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hmm, failed. Try the top-right/bottom-left pattern
|
||||||
|
int dimension = bitMatrix.getDimension();
|
||||||
|
formatInfoBits = 0;
|
||||||
|
int iMin = dimension - 8;
|
||||||
|
for (int i = dimension - 1; i >= iMin; i--) {
|
||||||
|
formatInfoBits = copyBit(i, 8, formatInfoBits);
|
||||||
|
}
|
||||||
|
for (int j = dimension - 7; j < dimension; j++) {
|
||||||
|
formatInfoBits = copyBit(8, j, formatInfoBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
|
||||||
|
if (parsedFormatInfo != null) {
|
||||||
|
return parsedFormatInfo;
|
||||||
|
}
|
||||||
|
throw new ReaderException("Could not decode format information");
|
||||||
|
}
|
||||||
|
|
||||||
|
Version readVersion() throws ReaderException {
|
||||||
|
|
||||||
|
if (parsedVersion != null) {
|
||||||
|
return parsedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dimension = bitMatrix.getDimension();
|
||||||
|
|
||||||
|
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;
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
int jMin = dimension - 11;
|
||||||
|
for (int j = dimension - 9; j >= jMin; j--) {
|
||||||
|
versionBits = copyBit(i, j, versionBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedVersion = Version.decodeVersionInformation(versionBits);
|
||||||
|
if (parsedVersion != null) {
|
||||||
|
return parsedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||||
|
versionBits = 0;
|
||||||
|
for (int j = 5; j >= 0; j--) {
|
||||||
|
int iMin = dimension - 11;
|
||||||
|
for (int i = dimension - 11; i >= iMin; i--) {
|
||||||
|
versionBits = copyBit(i, j, versionBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedVersion = Version.decodeVersionInformation(versionBits);
|
||||||
|
if (parsedVersion != null) {
|
||||||
|
return parsedVersion;
|
||||||
|
}
|
||||||
|
throw new ReaderException("Could not decode version");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int copyBit(int i, int j, int versionBits) {
|
||||||
|
return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] readCodewords() throws ReaderException {
|
||||||
|
|
||||||
|
FormatInformation formatInfo = readFormatInformation();
|
||||||
|
Version version = readVersion();
|
||||||
|
|
||||||
|
DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
|
||||||
|
int dimension = bitMatrix.getDimension();
|
||||||
|
dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);
|
||||||
|
|
||||||
|
BitMatrix functionPattern = version.buildFunctionPattern();
|
||||||
|
|
||||||
|
boolean readingUp = true;
|
||||||
|
byte[] result = new byte[version.getTotalCodewords()];
|
||||||
|
int resultOffset = 0;
|
||||||
|
int currentByte = 0;
|
||||||
|
int bitsRead = 0;
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
for (int count = 0; count < dimension; count++) {
|
||||||
|
int i = readingUp ? dimension - 1 - count : count;
|
||||||
|
for (int col = 0; col < 2; col++) {
|
||||||
|
if (!functionPattern.get(i, j - col)) {
|
||||||
|
bitsRead++;
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (bitMatrix.get(i, j - col)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
if (bitsRead == 8) {
|
||||||
|
result[resultOffset++] = (byte) currentByte;
|
||||||
|
bitsRead = 0;
|
||||||
|
currentByte = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readingUp = !readingUp; // switch directions
|
||||||
|
}
|
||||||
|
if (resultOffset != version.getTotalCodewords()) {
|
||||||
|
throw new ReaderException("Did not read all codewords");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
84
core/src/com/google/zxing/qrcode/decoder/BitSource.java
Executable file
84
core/src/com/google/zxing/qrcode/decoder/BitSource.java
Executable file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
|
||||||
|
* number of bits read is not often a multiple of 8.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class BitSource {
|
||||||
|
|
||||||
|
private final byte[] bytes;
|
||||||
|
private int byteOffset;
|
||||||
|
private int bitOffset;
|
||||||
|
|
||||||
|
BitSource(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalArgumentException if numBits isn't in [1,32]
|
||||||
|
*/
|
||||||
|
int readBits(int numBits) {
|
||||||
|
if (numBits < 1 || numBits > 32) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
// First, read remainder from current byte
|
||||||
|
if (bitOffset > 0) {
|
||||||
|
int bitsLeft = 8 - bitOffset;
|
||||||
|
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
|
||||||
|
int bitsToNotRead = bitsLeft - toRead;
|
||||||
|
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
|
||||||
|
result = (bytes[byteOffset] & mask) >> bitsToNotRead;
|
||||||
|
numBits -= toRead;
|
||||||
|
bitOffset += toRead;
|
||||||
|
if (bitOffset == 8) {
|
||||||
|
bitOffset = 0;
|
||||||
|
byteOffset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next read whole bytes
|
||||||
|
if (numBits > 0) {
|
||||||
|
while (numBits >= 8) {
|
||||||
|
result = (result << 8) | (bytes[byteOffset] & 0xFF);
|
||||||
|
byteOffset++;
|
||||||
|
numBits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally read a partial byte
|
||||||
|
if (numBits > 0) {
|
||||||
|
int bitsToNotRead = 8 - numBits;
|
||||||
|
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
|
||||||
|
result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
|
||||||
|
bitOffset += numBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int available() {
|
||||||
|
return 8 * (bytes.length - byteOffset) - bitOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
core/src/com/google/zxing/qrcode/decoder/DataBlock.java
Executable file
110
core/src/com/google/zxing/qrcode/decoder/DataBlock.java
Executable file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class DataBlock {
|
||||||
|
|
||||||
|
private final int numDataCodewords;
|
||||||
|
private final byte[] codewords;
|
||||||
|
|
||||||
|
private DataBlock(int numDataCodewords, byte[] codewords) {
|
||||||
|
this.numDataCodewords = numDataCodewords;
|
||||||
|
this.codewords = codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DataBlock[] getDataBlocks(byte[] rawCodewords,
|
||||||
|
Version version,
|
||||||
|
ErrorCorrectionLevel ecLevel) {
|
||||||
|
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||||
|
int totalBlocks = 0;
|
||||||
|
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||||
|
for (int i = 0; i < ecBlockArray.length; i++) {
|
||||||
|
totalBlocks += ecBlockArray[i].getCount();
|
||||||
|
}
|
||||||
|
DataBlock[] result = new DataBlock[totalBlocks];
|
||||||
|
int numResultBlocks = 0;
|
||||||
|
for (int j = 0; j < ecBlockArray.length; j++) {
|
||||||
|
Version.ECB ecBlock = ecBlockArray[j];
|
||||||
|
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||||
|
int numDataCodewords = ecBlock.getDataCodewords();
|
||||||
|
int numBlockCodewords = ecBlocks.getECCodewords() + 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;
|
||||||
|
}
|
||||||
|
if (numCodewords != shorterBlocksTotalCodewords + 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Data block sizes differ by more than 1");
|
||||||
|
}
|
||||||
|
longerBlocksStartAt--;
|
||||||
|
}
|
||||||
|
longerBlocksStartAt++;
|
||||||
|
|
||||||
|
int shorterBlocksNumDataCodewords =
|
||||||
|
shorterBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||||
|
// 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++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawCodewordsOffset != rawCodewords.length) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumDataCodewords() {
|
||||||
|
return numDataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getCodewords() {
|
||||||
|
return codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
245
core/src/com/google/zxing/qrcode/decoder/DataMask.java
Executable file
245
core/src/com/google/zxing/qrcode/decoder/DataMask.java
Executable file
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
abstract class DataMask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006 6.8.1
|
||||||
|
*/
|
||||||
|
private static final DataMask[] DATA_MASKS = new DataMask[]{
|
||||||
|
new DataMask000(),
|
||||||
|
new DataMask001(),
|
||||||
|
new DataMask010(),
|
||||||
|
new DataMask011(),
|
||||||
|
new DataMask100(),
|
||||||
|
new DataMask101(),
|
||||||
|
new DataMask110(),
|
||||||
|
new DataMask111(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private DataMask() {
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void unmaskBitMatrix(int[] bits, int dimension);
|
||||||
|
|
||||||
|
static DataMask forReference(int reference) {
|
||||||
|
if (reference < 0 || reference > 7) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return DATA_MASKS[reference];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 000: mask bits for which (i + j) mod 2 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask000 extends DataMask {
|
||||||
|
private static final int BITMASK = 0x55555555; // = 010101...
|
||||||
|
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
// This one's easy. Because the dimension of BitMatrix is always odd,
|
||||||
|
// we can merely flip every other bit
|
||||||
|
int max = bits.length;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
bits[i] ^= BITMASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 001: mask bits for which j mod 2 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask001 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
if ((i & 0x01) == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 010: mask bits for which j mod 3 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask010 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
boolean columnMasked = j % 3 == 0;
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
if (columnMasked) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 011: mask bits for which (i + j) mod 3 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask011 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
if ((i + j) % 3 == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 100: mask bits for which (i/2 + j/3) mod 2 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask100 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
int jComponent = j / 3;
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
if (((i >> 1 + jComponent) & 0x01) == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 101: mask bits for which ij mod 2 + ij mod 3 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask101 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int product = i * j;
|
||||||
|
if (((product & 0x01) == 0) && product % 3 == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 110: mask bits for which (ij mod 2 + ij mod 3) mod 2 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask110 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int product = i * j;
|
||||||
|
if ((((product & 0x01) + product % 3) & 0x01) == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 111: mask bits for which ((i+j)mod 2 + ij mod 3) mod 2 == 0
|
||||||
|
*/
|
||||||
|
private static class DataMask111 extends DataMask {
|
||||||
|
void unmaskBitMatrix(int[] bits, int dimension) {
|
||||||
|
int bitMask = 0;
|
||||||
|
int count = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
if (((((i + j) & 0x01) + (i * j) % 3) & 0x01) == 0) {
|
||||||
|
bitMask |= 1 << count;
|
||||||
|
}
|
||||||
|
if (++count == 32) {
|
||||||
|
bits[offset++] ^= bitMask;
|
||||||
|
count = 0;
|
||||||
|
bitMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[offset] ^= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.ReaderException;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006, 6.4.3 - 6.4.7
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class DecodedBitStreamParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006, 6.4.4 Table 5
|
||||||
|
*/
|
||||||
|
private static final char[] ALPHANUMERIC_CHARS = new char[]{
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||||
|
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
||||||
|
};
|
||||||
|
private static final String SHIFT_JIS = "Shift_JIS";
|
||||||
|
private static final boolean ASSUME_SHIFT_JIS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String platformDefault = System.getProperty("file.encoding");
|
||||||
|
ASSUME_SHIFT_JIS = SHIFT_JIS.equalsIgnoreCase(platformDefault) ||
|
||||||
|
"EUC-JP".equalsIgnoreCase(platformDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DecodedBitStreamParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static String decode(byte[] bytes, Version version) throws ReaderException {
|
||||||
|
BitSource bits = new BitSource(bytes);
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
Mode mode;
|
||||||
|
do {
|
||||||
|
// While still another segment to read...
|
||||||
|
mode = Mode.forBits(bits.readBits(4));
|
||||||
|
if (!mode.equals(Mode.TERMINATOR)) {
|
||||||
|
int count = bits.readBits(mode.getCharacterCountBits(version));
|
||||||
|
if (mode.equals(Mode.NUMERIC)) {
|
||||||
|
decodeNumericSegment(bits, result, count);
|
||||||
|
} else if (mode.equals(Mode.ALPHANUMERIC)) {
|
||||||
|
decodeAlphanumericSegment(bits, result, count);
|
||||||
|
} else if (mode.equals(Mode.BYTE)) {
|
||||||
|
decodeByteSegment(bits, result, count);
|
||||||
|
} else if (mode.equals(Mode.KANJI)) {
|
||||||
|
decodeKanjiSegment(bits, result, count);
|
||||||
|
} else {
|
||||||
|
throw new ReaderException("Unsupported mode indicator: " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!mode.equals(Mode.TERMINATOR));
|
||||||
|
|
||||||
|
/*
|
||||||
|
int bitsLeft = bits.available();
|
||||||
|
if (bitsLeft > 0) {
|
||||||
|
if (bitsLeft > 6 || bits.readBits(bitsLeft) != 0) {
|
||||||
|
throw new ReaderException("Excess bits or non-zero bits after terminator mode indicator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decodeKanjiSegment(BitSource bits,
|
||||||
|
StringBuffer result,
|
||||||
|
int count) throws ReaderException {
|
||||||
|
byte[] buffer = new byte[2 * count];
|
||||||
|
int offset = 0;
|
||||||
|
while (count > 0) {
|
||||||
|
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(new String(buffer, "Shift_JIS"));
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
throw new ReaderException("Can't decode SHIFT_JIS string: " + uee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decodeByteSegment(BitSource bits,
|
||||||
|
StringBuffer result,
|
||||||
|
int count) throws ReaderException {
|
||||||
|
byte[] readBytes = new byte[count];
|
||||||
|
if (count << 3 > bits.available()) {
|
||||||
|
throw new ReaderException("Count too large: " + count);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
readBytes[i] = (byte) bits.readBits(8);
|
||||||
|
}
|
||||||
|
// 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.
|
||||||
|
String encoding = guessEncoding(readBytes);
|
||||||
|
try {
|
||||||
|
result.append(new String(readBytes, encoding));
|
||||||
|
} catch (UnsupportedEncodingException uce) {
|
||||||
|
throw new ReaderException(uce.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decodeAlphanumericSegment(BitSource bits,
|
||||||
|
StringBuffer result,
|
||||||
|
int count) {
|
||||||
|
// Read two characters at a time
|
||||||
|
while (count > 1) {
|
||||||
|
int nextTwoCharsBits = bits.readBits(11);
|
||||||
|
result.append(ALPHANUMERIC_CHARS[nextTwoCharsBits / 45]);
|
||||||
|
result.append(ALPHANUMERIC_CHARS[nextTwoCharsBits % 45]);
|
||||||
|
count -= 2;
|
||||||
|
}
|
||||||
|
if (count == 1) {
|
||||||
|
// special case on char left
|
||||||
|
result.append(ALPHANUMERIC_CHARS[bits.readBits(6)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decodeNumericSegment(BitSource bits,
|
||||||
|
StringBuffer result,
|
||||||
|
int count) throws ReaderException {
|
||||||
|
while (count >= 3) {
|
||||||
|
int threeDigitsBits = bits.readBits(10);
|
||||||
|
if (threeDigitsBits >= 1000) {
|
||||||
|
throw new ReaderException("Illegal value for 3-digit unit: " + threeDigitsBits);
|
||||||
|
}
|
||||||
|
result.append(ALPHANUMERIC_CHARS[threeDigitsBits / 100]);
|
||||||
|
result.append(ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10]);
|
||||||
|
result.append(ALPHANUMERIC_CHARS[threeDigitsBits % 10]);
|
||||||
|
count -= 3;
|
||||||
|
}
|
||||||
|
if (count == 2) {
|
||||||
|
int twoDigitsBits = bits.readBits(7);
|
||||||
|
if (twoDigitsBits >= 100) {
|
||||||
|
throw new ReaderException("Illegal value for 2-digit unit: " + twoDigitsBits);
|
||||||
|
}
|
||||||
|
result.append(ALPHANUMERIC_CHARS[twoDigitsBits / 10]);
|
||||||
|
result.append(ALPHANUMERIC_CHARS[twoDigitsBits % 10]);
|
||||||
|
} else if (count == 1) {
|
||||||
|
int digitBits = bits.readBits(4);
|
||||||
|
if (digitBits >= 10) {
|
||||||
|
throw new ReaderException("Illegal value for digit unit: " + digitBits);
|
||||||
|
}
|
||||||
|
result.append(ALPHANUMERIC_CHARS[digitBits]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String guessEncoding(byte[] bytes) {
|
||||||
|
if (ASSUME_SHIFT_JIS) {
|
||||||
|
return SHIFT_JIS;
|
||||||
|
}
|
||||||
|
// For now, merely tries to distinguish ISO-8859-1 and Shift_JIS,
|
||||||
|
// which should be by far the most common encodings. ISO-8859-1
|
||||||
|
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
|
||||||
|
// uses this as a first byte of a two-byte character. If we see this
|
||||||
|
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
|
||||||
|
int length = bytes.length;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int value = bytes[i] & 0xFF;
|
||||||
|
if (value >= 0x80 && value <= 0x9F && i < length - 1) {
|
||||||
|
// ISO-8859-1 shouldn't use this, but before we decide it is Shift_JIS,
|
||||||
|
// just double check that it is followed by a byte that's valid in
|
||||||
|
// the Shift_JIS encoding
|
||||||
|
int nextValue = bytes[i + 1] & 0xFF;
|
||||||
|
if ((value & 0x1) == 0) {
|
||||||
|
// if even,
|
||||||
|
if (nextValue >= 0x40 && nextValue <= 0x9E) {
|
||||||
|
return SHIFT_JIS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nextValue >= 0x9F && nextValue <= 0x7C) {
|
||||||
|
return SHIFT_JIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "ISO-8859-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
88
core/src/com/google/zxing/qrcode/decoder/Decoder.java
Normal file
88
core/src/com/google/zxing/qrcode/decoder/Decoder.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class Decoder {
|
||||||
|
|
||||||
|
private Decoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(boolean[][] image) throws ReaderException {
|
||||||
|
int dimension = image.length;
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image[i][j]) {
|
||||||
|
bits.set(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decode(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(BitMatrix bits) throws ReaderException {
|
||||||
|
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||||
|
Version version = parser.readVersion();
|
||||||
|
ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
|
||||||
|
byte[] codewords = parser.readCodewords();
|
||||||
|
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
|
||||||
|
int totalBytes = 0;
|
||||||
|
for (int i = 0; i < dataBlocks.length; i++) {
|
||||||
|
totalBytes += dataBlocks[i].getNumDataCodewords();
|
||||||
|
}
|
||||||
|
byte[] resultBytes = new byte[totalBytes];
|
||||||
|
int resultOffset = 0;
|
||||||
|
for (int j = 0; j < dataBlocks.length; j++) {
|
||||||
|
DataBlock dataBlock = dataBlocks[j];
|
||||||
|
byte[] codewordBytes = dataBlock.getCodewords();
|
||||||
|
int numDataCodewords = dataBlock.getNumDataCodewords();
|
||||||
|
correctErrors(codewordBytes, numDataCodewords);
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
resultBytes[resultOffset++] = codewordBytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodedBitStreamParser.decode(resultBytes, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void correctErrors(byte[] codewordBytes, int numDataCodewords)
|
||||||
|
throws ReaderException {
|
||||||
|
int numCodewords = codewordBytes.length;
|
||||||
|
int[] codewordsInts = new int[numCodewords];
|
||||||
|
for (int i = 0; i < numCodewords; i++) {
|
||||||
|
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||||
|
}
|
||||||
|
int numECCodewords = codewordBytes.length - numDataCodewords;
|
||||||
|
try {
|
||||||
|
ReedSolomonDecoder.decode(codewordsInts, numECCodewords);
|
||||||
|
} catch (ReedSolomonException rse) {
|
||||||
|
throw new ReaderException(rse.toString());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
codewordBytes[i] = (byte) codewordsInts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
|
||||||
|
* defined by the QR code standard.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class ErrorCorrectionLevel {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
/** L = ~7% correction */
|
||||||
|
static final ErrorCorrectionLevel L = new ErrorCorrectionLevel(0);
|
||||||
|
/** M = ~15% correction */
|
||||||
|
static final ErrorCorrectionLevel M = new ErrorCorrectionLevel(1);
|
||||||
|
/** Q = ~25% correction */
|
||||||
|
static final ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2);
|
||||||
|
/** H = ~30% correction */
|
||||||
|
static final ErrorCorrectionLevel H = new ErrorCorrectionLevel(3);
|
||||||
|
|
||||||
|
private static final ErrorCorrectionLevel[] FOR_BITS = new ErrorCorrectionLevel[] { M, L, H, Q };
|
||||||
|
|
||||||
|
private final int ordinal;
|
||||||
|
|
||||||
|
private ErrorCorrectionLevel(final int ordinal) {
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ordinal() {
|
||||||
|
return ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorCorrectionLevel forBits(int bits) {
|
||||||
|
return FOR_BITS[bits];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
147
core/src/com/google/zxing/qrcode/decoder/FormatInformation.java
Normal file
147
core/src/com/google/zxing/qrcode/decoder/FormatInformation.java
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class FormatInformation {
|
||||||
|
|
||||||
|
private static final int FORMAT_INFO_MASK_QR = 0x5412;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006, Annex C, Table C.1
|
||||||
|
*/
|
||||||
|
private static final int[][] FORMAT_INFO_DECODE_LOOKUP = new int[][]{
|
||||||
|
{0x5412, 0x00},
|
||||||
|
{0x5125, 0x01},
|
||||||
|
{0x5E7C, 0x02},
|
||||||
|
{0x5B4B, 0x03},
|
||||||
|
{0x45F9, 0x04},
|
||||||
|
{0x40CE, 0x05},
|
||||||
|
{0x4F97, 0x06},
|
||||||
|
{0x4AA0, 0x07},
|
||||||
|
{0x77C4, 0x08},
|
||||||
|
{0x72F3, 0x09},
|
||||||
|
{0x7DAA, 0x0A},
|
||||||
|
{0x789D, 0x0B},
|
||||||
|
{0x662F, 0x0C},
|
||||||
|
{0x6318, 0x0D},
|
||||||
|
{0x6C41, 0x0E},
|
||||||
|
{0x6976, 0x0F},
|
||||||
|
{0x1689, 0x10},
|
||||||
|
{0x13BE, 0x11},
|
||||||
|
{0x1CE7, 0x12},
|
||||||
|
{0x19D0, 0x13},
|
||||||
|
{0x0762, 0x14},
|
||||||
|
{0x0255, 0x15},
|
||||||
|
{0x0D0C, 0x16},
|
||||||
|
{0x083B, 0x17},
|
||||||
|
{0x355F, 0x18},
|
||||||
|
{0x3068, 0x19},
|
||||||
|
{0x3F31, 0x1A},
|
||||||
|
{0x3A06, 0x1B},
|
||||||
|
{0x24B4, 0x1C},
|
||||||
|
{0x2183, 0x1D},
|
||||||
|
{0x2EDA, 0x1E},
|
||||||
|
{0x2BED, 0x1F},
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] BITS_SET_IN_HALF_BYTE =
|
||||||
|
new int[]{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
||||||
|
|
||||||
|
private final ErrorCorrectionLevel errorCorrectionLevel;
|
||||||
|
private final byte dataMask;
|
||||||
|
|
||||||
|
private FormatInformation(int formatInfo) {
|
||||||
|
// Bits 3,4
|
||||||
|
errorCorrectionLevel =
|
||||||
|
ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
|
||||||
|
// Bottom 3 bits
|
||||||
|
dataMask = (byte) (formatInfo & 0x07);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int numBitsDiffering(int a, int b) {
|
||||||
|
a ^= b;
|
||||||
|
return
|
||||||
|
BITS_SET_IN_HALF_BYTE[a & 0x0F] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] +
|
||||||
|
BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static FormatInformation decodeFormatInformation(int rawFormatInfo) {
|
||||||
|
FormatInformation formatInfo = doDecodeFormatInformation(rawFormatInfo);
|
||||||
|
if (formatInfo != null) {
|
||||||
|
return formatInfo;
|
||||||
|
}
|
||||||
|
// Should return null, but, some QR codes apparently
|
||||||
|
// do not mask this info. Try again, first masking the raw bits so
|
||||||
|
// the function will unmask
|
||||||
|
return doDecodeFormatInformation(rawFormatInfo ^ FORMAT_INFO_MASK_QR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormatInformation doDecodeFormatInformation(
|
||||||
|
int rawFormatInfo) {
|
||||||
|
// Unmask:
|
||||||
|
int unmaskedFormatInfo = rawFormatInfo ^ FORMAT_INFO_MASK_QR;
|
||||||
|
int bestDifference = Integer.MAX_VALUE;
|
||||||
|
int bestFormatInfo = 0;
|
||||||
|
for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
|
||||||
|
int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
|
||||||
|
int targetInfo = decodeInfo[0];
|
||||||
|
if (targetInfo == unmaskedFormatInfo) {
|
||||||
|
return new FormatInformation(decodeInfo[1]);
|
||||||
|
}
|
||||||
|
int bitsDifference = numBitsDiffering(unmaskedFormatInfo, targetInfo);
|
||||||
|
if (bitsDifference < bestDifference) {
|
||||||
|
bestFormatInfo = decodeInfo[1];
|
||||||
|
bestDifference = bitsDifference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestDifference <= 3) {
|
||||||
|
return new FormatInformation(bestFormatInfo);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCorrectionLevel getErrorCorrectionLevel() {
|
||||||
|
return errorCorrectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte getDataMask() {
|
||||||
|
return dataMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof FormatInformation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FormatInformation other = (FormatInformation) o;
|
||||||
|
return this.errorCorrectionLevel == other.errorCorrectionLevel &&
|
||||||
|
this.dataMask == other.dataMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
core/src/com/google/zxing/qrcode/decoder/Mode.java
Normal file
73
core/src/com/google/zxing/qrcode/decoder/Mode.java
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.ReaderException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class Mode {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
static final Mode TERMINATOR = new Mode(new int[] {0, 0, 0}); // Not really a mode...
|
||||||
|
static final Mode NUMERIC = new Mode(new int[] {10, 12, 14});
|
||||||
|
static final Mode ALPHANUMERIC = new Mode(new int[] {9, 11, 13});
|
||||||
|
static final Mode BYTE = new Mode(new int[] {8, 16, 16});
|
||||||
|
static final Mode KANJI = new Mode(new int[] {8, 10, 12});
|
||||||
|
|
||||||
|
private int[] characterCountBitsForVersions;
|
||||||
|
|
||||||
|
private Mode(int[] characterCountBitsForVersions) {
|
||||||
|
this.characterCountBitsForVersions = characterCountBitsForVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mode forBits(int bits) throws ReaderException {
|
||||||
|
switch (bits) {
|
||||||
|
case 0x0:
|
||||||
|
return TERMINATOR;
|
||||||
|
case 0x1:
|
||||||
|
return NUMERIC;
|
||||||
|
case 0x2:
|
||||||
|
return ALPHANUMERIC;
|
||||||
|
case 0x4:
|
||||||
|
return BYTE;
|
||||||
|
case 0x8:
|
||||||
|
return KANJI;
|
||||||
|
default:
|
||||||
|
throw new ReaderException("Illegal mode bits: " + bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCharacterCountBits(Version version) {
|
||||||
|
int number = version.getVersionNumber();
|
||||||
|
int offset;
|
||||||
|
if (number <= 9) {
|
||||||
|
offset = 0;
|
||||||
|
} else if (number <= 26) {
|
||||||
|
offset = 1;
|
||||||
|
} else {
|
||||||
|
offset = 2;
|
||||||
|
}
|
||||||
|
return characterCountBitsForVersions[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
562
core/src/com/google/zxing/qrcode/decoder/Version.java
Executable file
562
core/src/com/google/zxing/qrcode/decoder/Version.java
Executable file
|
@ -0,0 +1,562 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006 Annex D
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class Version {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006 Annex D.
|
||||||
|
* Element i represents the raw version bits that specify version i + 7
|
||||||
|
*/
|
||||||
|
private static final int[] VERSION_DECODE_INFO = new int[]{
|
||||||
|
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 final Version[] VERSIONS = buildVersions();
|
||||||
|
|
||||||
|
private final int versionNumber;
|
||||||
|
private final int[] alignmentPatternCenters;
|
||||||
|
private final ECBlocks[] ecBlocks;
|
||||||
|
private final int totalCodewords;
|
||||||
|
|
||||||
|
private Version(int versionNumber,
|
||||||
|
int[] alignmentPatternCenters,
|
||||||
|
ECBlocks ecBlocks1,
|
||||||
|
ECBlocks ecBlocks2,
|
||||||
|
ECBlocks ecBlocks3,
|
||||||
|
ECBlocks ecBlocks4) {
|
||||||
|
this.versionNumber = versionNumber;
|
||||||
|
this.alignmentPatternCenters = alignmentPatternCenters;
|
||||||
|
this.ecBlocks = new ECBlocks[] { ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4 };
|
||||||
|
int total = 0;
|
||||||
|
ECBlocks levelLECBlocks = ecBlocks1; // L,M,Q,H -- all the same total
|
||||||
|
int ecCodewords = levelLECBlocks.ecCodewords;
|
||||||
|
ECB[] ecbArray = levelLECBlocks.ecBlocks;
|
||||||
|
for (int i = 0; i < ecbArray.length; i++) {
|
||||||
|
ECB ecBlock = ecbArray[i];
|
||||||
|
total += ecBlock.count * (ecBlock.dataCodewords + ecCodewords);
|
||||||
|
}
|
||||||
|
this.totalCodewords = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVersionNumber() {
|
||||||
|
return versionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getAlignmentPatternCenters() {
|
||||||
|
return alignmentPatternCenters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalCodewords() {
|
||||||
|
return totalCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDimensionForVersion() {
|
||||||
|
return 17 + 4 * versionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
|
||||||
|
return ecBlocks[ecLevel.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Version getProvisionalVersionForDimension(int dimension)
|
||||||
|
throws ReaderException {
|
||||||
|
if (dimension % 4 != 1) {
|
||||||
|
throw new ReaderException("Dimension must be 1 mod 4");
|
||||||
|
}
|
||||||
|
return getVersionForNumber((dimension - 17) >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Version getVersionForNumber(int versionNumber)
|
||||||
|
throws ReaderException {
|
||||||
|
if (versionNumber < 1 || versionNumber > 40) {
|
||||||
|
throw new ReaderException(
|
||||||
|
"versionNumber must be between 1 and 40");
|
||||||
|
}
|
||||||
|
return VERSIONS[versionNumber - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static Version decodeVersionInformation(int versionBits)
|
||||||
|
throws ReaderException {
|
||||||
|
int bestDifference = Integer.MAX_VALUE;
|
||||||
|
int bestVersion = 0;
|
||||||
|
for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
|
||||||
|
int targetVersion = VERSION_DECODE_INFO[i];
|
||||||
|
if (targetVersion == versionBits) {
|
||||||
|
return getVersionForNumber(i + 7);
|
||||||
|
}
|
||||||
|
int bitsDifference =
|
||||||
|
FormatInformation.numBitsDiffering(versionBits, targetVersion);
|
||||||
|
if (bitsDifference < bestDifference) {
|
||||||
|
bestVersion = i + 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestDifference <= 3) {
|
||||||
|
return getVersionForNumber(bestVersion);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006 Annex E
|
||||||
|
*/
|
||||||
|
BitMatrix buildFunctionPattern() {
|
||||||
|
int dimension = getDimensionForVersion();
|
||||||
|
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(0, dimension - 8, 9, 8);
|
||||||
|
// Bottom left finder pattern + separator + format
|
||||||
|
bitMatrix.setRegion(dimension - 8, 0, 8, 9);
|
||||||
|
|
||||||
|
// 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)) {
|
||||||
|
// No alignment patterns near the three finder paterns
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bitMatrix.setRegion(i, alignmentPatternCenters[y] - 2, 5, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical timing pattern
|
||||||
|
bitMatrix.setRegion(9, 6, dimension - 17, 1);
|
||||||
|
// Horizontal timing pattern
|
||||||
|
bitMatrix.setRegion(6, 9, 1, dimension - 17);
|
||||||
|
|
||||||
|
if (versionNumber > 6) {
|
||||||
|
// Version info, top right
|
||||||
|
bitMatrix.setRegion(0, dimension - 11, 6, 3);
|
||||||
|
// Version info, bottom left
|
||||||
|
bitMatrix.setRegion(dimension - 11, 0, 3, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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>
|
||||||
|
*/
|
||||||
|
static final class ECBlocks {
|
||||||
|
private int ecCodewords;
|
||||||
|
private ECB[] ecBlocks;
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks };
|
||||||
|
}
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
int getECCodewords() {
|
||||||
|
return ecCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECB[] getECBlocks() {
|
||||||
|
return ecBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsualtes 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>
|
||||||
|
*/
|
||||||
|
static final class ECB {
|
||||||
|
private int count;
|
||||||
|
private int dataCodewords;
|
||||||
|
|
||||||
|
private ECB(int count, int dataCodewords) {
|
||||||
|
this.count = count;
|
||||||
|
this.dataCodewords = dataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDataCodewords() {
|
||||||
|
return dataCodewords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(versionNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 18004:2006 6.5.1 Table 9
|
||||||
|
*/
|
||||||
|
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, 74, 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, 1061),
|
||||||
|
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,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class AlignmentPattern implements ResultPoint {
|
||||||
|
|
||||||
|
private final float posX;
|
||||||
|
private final float posY;
|
||||||
|
private final float estimatedModuleSize;
|
||||||
|
|
||||||
|
AlignmentPattern(float posX, float posY, float estimatedModuleSize) {
|
||||||
|
this.posX = posX;
|
||||||
|
this.posY = posY;
|
||||||
|
this.estimatedModuleSize = estimatedModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return posX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY() {
|
||||||
|
return posY;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getEstimatedModuleSize() {
|
||||||
|
return estimatedModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean aboutEquals(float moduleSize, float i, float j) {
|
||||||
|
return
|
||||||
|
Math.abs(i - posY) <= moduleSize &&
|
||||||
|
Math.abs(j - posX) <= moduleSize &&
|
||||||
|
(Math.abs(moduleSize - estimatedModuleSize) <= 1.0f ||
|
||||||
|
Math.abs(moduleSize - estimatedModuleSize) / estimatedModuleSize <= 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>At the moment this only looks for the bottom-right alignment pattern.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is not thread-safe.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class AlignmentPatternFinder {
|
||||||
|
|
||||||
|
private final MonochromeBitmapSource image;
|
||||||
|
private final Vector possibleCenters;
|
||||||
|
private final int startX;
|
||||||
|
private final int startY;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private final float moduleSize;
|
||||||
|
|
||||||
|
AlignmentPatternFinder(MonochromeBitmapSource image,
|
||||||
|
int startX,
|
||||||
|
int startY,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
float moduleSize) {
|
||||||
|
this.image = image;
|
||||||
|
this.possibleCenters = new Vector(5);
|
||||||
|
this.startX = startX;
|
||||||
|
this.startY = startY;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.moduleSize = moduleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignmentPattern find() throws ReaderException {
|
||||||
|
int startX = this.startX;
|
||||||
|
int height = this.height;
|
||||||
|
int maxJ = startX + width;
|
||||||
|
int middleI = startY + (height >> 1);
|
||||||
|
BitArray luminanceRow = new BitArray(width);
|
||||||
|
int[] stateCount = new int[3]; // looking for 1 1 1
|
||||||
|
for (int iGen = 0; iGen < height; iGen++) {
|
||||||
|
// Search from middle outwards
|
||||||
|
int i = middleI +
|
||||||
|
((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
|
||||||
|
image.getBlackRow(i, luminanceRow, startX, width);
|
||||||
|
stateCount[0] = 0;
|
||||||
|
stateCount[1] = 0;
|
||||||
|
stateCount[2] = 0;
|
||||||
|
int currentState = 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 (!luminanceRow.get(j - startX) && j < maxJ) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
while (j < maxJ) {
|
||||||
|
if (luminanceRow.get(j - startX)) {
|
||||||
|
// Black pixel
|
||||||
|
if (currentState == 1) { // Counting black pixels
|
||||||
|
stateCount[currentState]++;
|
||||||
|
} 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.isEmpty()) {
|
||||||
|
return (AlignmentPattern) possibleCenters.elementAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ReaderException("Could not find alignment pattern");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float centerFromEnd(int[] stateCount, int end) {
|
||||||
|
return (float) (end - stateCount[2]) - stateCount[1] / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean foundPatternCross(int[] stateCount) {
|
||||||
|
float moduleSize = this.moduleSize;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (2.0f * Math.abs(moduleSize - stateCount[i]) >= moduleSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float crossCheckVertical(int startI, int centerJ, int maxCount) {
|
||||||
|
MonochromeBitmapSource image = this.image;
|
||||||
|
|
||||||
|
int maxI = image.getHeight();
|
||||||
|
int[] stateCount = new int[3];
|
||||||
|
int i = startI;
|
||||||
|
while (i >= 0 && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) {
|
||||||
|
stateCount[1]++;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (i < 0 || stateCount[1] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[0] <= maxCount) {
|
||||||
|
stateCount[0]++;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (stateCount[0] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = startI + 1;
|
||||||
|
while (i < maxI && image.isBlack(centerJ, i) &&
|
||||||
|
stateCount[1] <= maxCount) {
|
||||||
|
stateCount[1]++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i == maxI || stateCount[1] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (i < maxI && !image.isBlack(centerJ, i) &&
|
||||||
|
stateCount[2] <= maxCount) {
|
||||||
|
stateCount[2]++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (stateCount[2] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
foundPatternCross(stateCount) ?
|
||||||
|
centerFromEnd(stateCount, i) :
|
||||||
|
Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlignmentPattern handlePossibleCenter(int[] stateCount,
|
||||||
|
int i,
|
||||||
|
int j) {
|
||||||
|
float centerJ = centerFromEnd(stateCount, j);
|
||||||
|
float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1]);
|
||||||
|
if (!Float.isNaN(centerI)) {
|
||||||
|
float estimatedModuleSize = (float) (stateCount[0] +
|
||||||
|
stateCount[1] +
|
||||||
|
stateCount[2]) / 3.0f;
|
||||||
|
int max = possibleCenters.size();
|
||||||
|
for (int index = 0; index < max; index++) {
|
||||||
|
AlignmentPattern center = (AlignmentPattern) possibleCenters.elementAt(index);
|
||||||
|
// Look for about the same center and module size:
|
||||||
|
if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
|
||||||
|
return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hadn't found this before; save it
|
||||||
|
possibleCenters.addElement(new AlignmentPattern(centerJ, centerI, estimatedModuleSize));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class DefaultGridSampler extends GridSampler {
|
||||||
|
|
||||||
|
protected BitMatrix sampleGrid(MonochromeBitmapSource image,
|
||||||
|
FinderPattern topLeft,
|
||||||
|
FinderPattern topRight,
|
||||||
|
FinderPattern bottomLeft,
|
||||||
|
AlignmentPattern alignmentPattern,
|
||||||
|
int dimension) throws ReaderException {
|
||||||
|
float bottomRightX;
|
||||||
|
float bottomRightY;
|
||||||
|
if (alignmentPattern != null) {
|
||||||
|
bottomRightX = alignmentPattern.getX();
|
||||||
|
bottomRightY = alignmentPattern.getY();
|
||||||
|
} else {
|
||||||
|
// Don't have an alignment pattern, just make up the bottom-right point
|
||||||
|
bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
|
||||||
|
bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
float dimMinusThree = (float) dimension - 3.5f;
|
||||||
|
JAIPerspectiveTransform transform = JAIPerspectiveTransform.getQuadToQuad(
|
||||||
|
3.5f,
|
||||||
|
3.5f,
|
||||||
|
dimMinusThree,
|
||||||
|
3.5f,
|
||||||
|
3.5f,
|
||||||
|
dimMinusThree,
|
||||||
|
dimMinusThree - 3.0f,
|
||||||
|
dimMinusThree - 3.0f,
|
||||||
|
topLeft.getX(),
|
||||||
|
topLeft.getY(),
|
||||||
|
topRight.getX(),
|
||||||
|
topRight.getY(),
|
||||||
|
bottomLeft.getX(),
|
||||||
|
bottomLeft.getY(),
|
||||||
|
bottomRightX,
|
||||||
|
bottomRightY);
|
||||||
|
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
float[] points = new float[dimension << 1];
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int max = points.length;
|
||||||
|
float iValue = (float) i + 0.5f;
|
||||||
|
for (int j = 0; j < max; j += 2) {
|
||||||
|
points[j] = (float) j + 0.5f;
|
||||||
|
points[j + 1] = iValue;
|
||||||
|
}
|
||||||
|
transform.transform(points);
|
||||||
|
// Quick check to see if points transformed to something inside the image;
|
||||||
|
// sufficent to check the endpoints
|
||||||
|
checkEndpoint(image, points);
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
int offset = j << 1;
|
||||||
|
if (image.isBlack((int) points[offset], (int) points[offset + 1])) {
|
||||||
|
// Black(-ish) pixel
|
||||||
|
bits.set(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
263
core/src/com/google/zxing/qrcode/detector/Detector.java
Normal file
263
core/src/com/google/zxing/qrcode/detector/Detector.java
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.qrcode.decoder.Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class Detector {
|
||||||
|
|
||||||
|
private final MonochromeBitmapSource image;
|
||||||
|
|
||||||
|
public Detector(MonochromeBitmapSource image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DetectorResult detect() throws ReaderException {
|
||||||
|
|
||||||
|
MonochromeBitmapSource image = this.image;
|
||||||
|
|
||||||
|
FinderPatternFinder finder = new FinderPatternFinder(image);
|
||||||
|
FinderPatternInfo info = finder.find();
|
||||||
|
|
||||||
|
FinderPattern topLeft = info.getTopLeft();
|
||||||
|
FinderPattern topRight = info.getTopRight();
|
||||||
|
FinderPattern bottomLeft = info.getBottomLeft();
|
||||||
|
|
||||||
|
float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
|
||||||
|
int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
|
||||||
|
Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
|
||||||
|
int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;
|
||||||
|
|
||||||
|
// Guess where a "bottom right" finder pattern would have been
|
||||||
|
float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
|
||||||
|
float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
|
||||||
|
|
||||||
|
AlignmentPattern alignmentPattern = null;
|
||||||
|
// Anything above version 1 has an alignment pattern
|
||||||
|
if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
|
||||||
|
|
||||||
|
// Estimate that alignment pattern is closer by 3 modules
|
||||||
|
// from "bottom right" to known top left location
|
||||||
|
float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;
|
||||||
|
int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX()));
|
||||||
|
int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY()));
|
||||||
|
|
||||||
|
// Kind of arbitrary -- expand search radius before giving up
|
||||||
|
for (int i = 4; i <= 16; i <<= 1) {
|
||||||
|
try {
|
||||||
|
alignmentPattern = findAlignmentInRegion(moduleSize,
|
||||||
|
estAlignmentX,
|
||||||
|
estAlignmentY,
|
||||||
|
(float) i);
|
||||||
|
break;
|
||||||
|
} catch (ReaderException de) {
|
||||||
|
// try next round
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alignmentPattern == null) {
|
||||||
|
throw new ReaderException("Could not find alignment pattern");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GridSampler sampler = GridSampler.getInstance();
|
||||||
|
BitMatrix bits = sampler.sampleGrid(image,
|
||||||
|
topLeft,
|
||||||
|
topRight,
|
||||||
|
bottomLeft,
|
||||||
|
alignmentPattern,
|
||||||
|
dimension);
|
||||||
|
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
BufferedImage outImage =
|
||||||
|
new BufferedImage(dimension,
|
||||||
|
dimension,
|
||||||
|
BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (bits.get(i, j)) {
|
||||||
|
outImage.setRGB(j, i, 0xFF000000);
|
||||||
|
} else {
|
||||||
|
outImage.setRGB(j, i, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImageIO.write(outImage, "PNG",
|
||||||
|
new File("/home/srowen/out.png"));
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 int computeDimension(ResultPoint topLeft,
|
||||||
|
ResultPoint topRight,
|
||||||
|
ResultPoint bottomLeft,
|
||||||
|
float moduleSize)
|
||||||
|
throws ReaderException {
|
||||||
|
int tltrCentersDimension =
|
||||||
|
round(FinderPatternFinder.distance(topLeft, topRight) / moduleSize);
|
||||||
|
int tlblCentersDimension =
|
||||||
|
round(FinderPatternFinder.distance(topLeft, bottomLeft) / moduleSize);
|
||||||
|
int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
|
||||||
|
switch (dimension & 0x03) { // mod 4
|
||||||
|
case 0:
|
||||||
|
dimension++;
|
||||||
|
break;
|
||||||
|
// 1? do nothing
|
||||||
|
case 2:
|
||||||
|
dimension--;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
throw new ReaderException("Bad dimension: " + dimension);
|
||||||
|
}
|
||||||
|
return dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateModuleSize(ResultPoint topLeft,
|
||||||
|
ResultPoint topRight,
|
||||||
|
ResultPoint bottomLeft) {
|
||||||
|
// Take the average
|
||||||
|
return (calculateModuleSizeOneWay(topLeft, topRight) +
|
||||||
|
calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateModuleSizeOneWay(ResultPoint pattern,
|
||||||
|
ResultPoint otherPattern) {
|
||||||
|
float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(),
|
||||||
|
(int) pattern.getY(),
|
||||||
|
(int) otherPattern.getX(),
|
||||||
|
(int) otherPattern.getY());
|
||||||
|
float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(),
|
||||||
|
(int) otherPattern.getY(),
|
||||||
|
(int) pattern.getX(),
|
||||||
|
(int) pattern.getY());
|
||||||
|
if (Float.isNaN(moduleSizeEst1)) {
|
||||||
|
return moduleSizeEst2;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(moduleSizeEst2)) {
|
||||||
|
return moduleSizeEst1;
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
|
||||||
|
float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
|
||||||
|
result += sizeOfBlackWhiteBlackRun(fromX, fromY, fromX - (toX - fromX), fromY - (toY - fromY));
|
||||||
|
return result - 1.0f; // -1 because we counted the middle pixel twice
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
boolean 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 ystep = fromY < toY ? 1 : -1;
|
||||||
|
int xstep = fromX < toX ? 1 : -1;
|
||||||
|
int state = 0; // In black pixels, looking for white, first or second time
|
||||||
|
for (int x = fromX, y = fromY; x != toX; x += xstep) {
|
||||||
|
|
||||||
|
int realX = steep ? y : x;
|
||||||
|
int realY = steep ? x : y;
|
||||||
|
if (state == 1) { // In white pixels, looking for black
|
||||||
|
if (image.isBlack(realX, realY)) {
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!image.isBlack(realX, realY)) {
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 3) { // Found black, white, black, and stumbled back onto white; done
|
||||||
|
int diffX = x - fromX;
|
||||||
|
int diffY = y - fromY;
|
||||||
|
return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
|
||||||
|
}
|
||||||
|
error += dy;
|
||||||
|
if (error > 0) {
|
||||||
|
y += ystep;
|
||||||
|
error -= dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hmm, couldn't find all of what we wanted -- don't know
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,
|
||||||
|
int estAlignmentX,
|
||||||
|
int estAlignmentY,
|
||||||
|
float allowanceFactor)
|
||||||
|
throws ReaderException {
|
||||||
|
// Look for an alignment pattern (3 modules in size) around where it
|
||||||
|
// should be
|
||||||
|
int allowance = (int) (allowanceFactor * overallEstModuleSize);
|
||||||
|
int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
|
||||||
|
int alignmentAreaRightX = Math.min(image.getWidth() - 1,
|
||||||
|
estAlignmentX + allowance);
|
||||||
|
int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
|
||||||
|
int alignmentAreaBottomY = Math.min(image.getHeight() - 1,
|
||||||
|
estAlignmentY + allowance);
|
||||||
|
|
||||||
|
AlignmentPatternFinder alignmentFinder =
|
||||||
|
new AlignmentPatternFinder(
|
||||||
|
image,
|
||||||
|
alignmentAreaLeftX,
|
||||||
|
alignmentAreaTopY,
|
||||||
|
alignmentAreaRightX - alignmentAreaLeftX,
|
||||||
|
alignmentAreaBottomY - alignmentAreaTopY,
|
||||||
|
overallEstModuleSize);
|
||||||
|
return alignmentFinder.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends up being a bit faster than Math.round()
|
||||||
|
*/
|
||||||
|
private static int round(float d) {
|
||||||
|
return (int) (d + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
|
||||||
|
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
|
||||||
|
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class DetectorResult {
|
||||||
|
|
||||||
|
private final BitMatrix bits;
|
||||||
|
private final ResultPoint[] points;
|
||||||
|
|
||||||
|
DetectorResult(BitMatrix bits, ResultPoint[] points) {
|
||||||
|
this.bits = bits;
|
||||||
|
this.points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix getBits() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultPoint[] getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
65
core/src/com/google/zxing/qrcode/detector/FinderPattern.java
Normal file
65
core/src/com/google/zxing/qrcode/detector/FinderPattern.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class FinderPattern implements ResultPoint {
|
||||||
|
|
||||||
|
private final float posX;
|
||||||
|
private final float posY;
|
||||||
|
private final float estimatedModuleSize;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
FinderPattern(float posX, float posY, float estimatedModuleSize) {
|
||||||
|
this.posX = posX;
|
||||||
|
this.posY = posY;
|
||||||
|
this.estimatedModuleSize = estimatedModuleSize;
|
||||||
|
this.count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return posX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY() {
|
||||||
|
return posY;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getEstimatedModuleSize() {
|
||||||
|
return estimatedModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementCount() {
|
||||||
|
this.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean aboutEquals(float moduleSize, float i, float j) {
|
||||||
|
return Math.abs(i - posY) <= moduleSize &&
|
||||||
|
Math.abs(j - posX) <= moduleSize &&
|
||||||
|
(Math.abs(moduleSize - estimatedModuleSize) <= 1.0f ||
|
||||||
|
Math.abs(moduleSize - estimatedModuleSize) / estimatedModuleSize <= 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
474
core/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
Executable file
474
core/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
Executable file
|
@ -0,0 +1,474 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
import com.google.zxing.common.Collections;
|
||||||
|
import com.google.zxing.common.Comparator;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class is not thread-safe and should not be reused.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class FinderPatternFinder {
|
||||||
|
|
||||||
|
private static final int CENTER_QUORUM = 2;
|
||||||
|
private static final int BIG_SKIP = 3;
|
||||||
|
|
||||||
|
private final MonochromeBitmapSource image;
|
||||||
|
private final Vector possibleCenters;
|
||||||
|
private boolean hasSkipped;
|
||||||
|
|
||||||
|
FinderPatternFinder(MonochromeBitmapSource image) {
|
||||||
|
this.image = image;
|
||||||
|
this.possibleCenters = new Vector(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinderPatternInfo find() throws ReaderException {
|
||||||
|
int maxI = image.getHeight();
|
||||||
|
int maxJ = image.getWidth();
|
||||||
|
int[] stateCount = new int[5]; // looking for 1 1 3 1 1
|
||||||
|
boolean done = false;
|
||||||
|
// We can afford to examine every few lines until we've started finding
|
||||||
|
// the patterns
|
||||||
|
int iSkip = BIG_SKIP;
|
||||||
|
for (int i = iSkip - 1; i < maxI && !done; i += iSkip) {
|
||||||
|
BitArray luminanceRow = image.getBlackRow(i, null, 0, maxJ);
|
||||||
|
stateCount[0] = 0;
|
||||||
|
stateCount[1] = 0;
|
||||||
|
stateCount[2] = 0;
|
||||||
|
stateCount[3] = 0;
|
||||||
|
stateCount[4] = 0;
|
||||||
|
int currentState = 0;
|
||||||
|
for (int j = 0; j < maxJ; j++) {
|
||||||
|
if (luminanceRow.get(j)) {
|
||||||
|
// 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
|
||||||
|
boolean confirmed =
|
||||||
|
handlePossibleCenter(stateCount, i, j);
|
||||||
|
if (confirmed) {
|
||||||
|
iSkip = 1; // Go back to examining each line
|
||||||
|
if (hasSkipped) {
|
||||||
|
done = haveMulitplyConfirmedCenters();
|
||||||
|
} 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 {
|
||||||
|
// Advance to next black pixel
|
||||||
|
do {
|
||||||
|
j++;
|
||||||
|
} while (j < maxJ && !luminanceRow.get(j));
|
||||||
|
j--; // back up to that last white pixel
|
||||||
|
}
|
||||||
|
// Clear state to start looking again
|
||||||
|
currentState = 0;
|
||||||
|
stateCount[0] = 0;
|
||||||
|
stateCount[1] = 0;
|
||||||
|
stateCount[2] = 0;
|
||||||
|
stateCount[3] = 0;
|
||||||
|
stateCount[4] = 0;
|
||||||
|
} else { // No, shift counts back by two
|
||||||
|
stateCount[0] = stateCount[2];
|
||||||
|
stateCount[1] = stateCount[3];
|
||||||
|
stateCount[2] = stateCount[4];
|
||||||
|
stateCount[3] = 1;
|
||||||
|
stateCount[4] = 0;
|
||||||
|
currentState = 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stateCount[++currentState]++;
|
||||||
|
}
|
||||||
|
} else { // Counting white pixels
|
||||||
|
stateCount[currentState]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foundPatternCross(stateCount)) {
|
||||||
|
boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);
|
||||||
|
if (confirmed) {
|
||||||
|
iSkip = stateCount[0];
|
||||||
|
if (hasSkipped) {
|
||||||
|
// Found a third one
|
||||||
|
done = haveMulitplyConfirmedCenters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FinderPattern[] patternInfo = selectBestPatterns();
|
||||||
|
patternInfo = orderBestPatterns(patternInfo);
|
||||||
|
float totalModuleSize = 0.0f;
|
||||||
|
for (int i = 0; i < patternInfo.length; i++) {
|
||||||
|
totalModuleSize += patternInfo[i].getEstimatedModuleSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FinderPatternInfo(totalModuleSize / (float) patternInfo.length,
|
||||||
|
patternInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float centerFromEnd(int[] stateCount, int end) {
|
||||||
|
return (float) (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean foundPatternCross(int[] stateCount) {
|
||||||
|
int totalModuleSize = 0;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (stateCount[i] == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
totalModuleSize += stateCount[i];
|
||||||
|
}
|
||||||
|
if (totalModuleSize < 7) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int moduleSize = totalModuleSize / 7;
|
||||||
|
// Allow less than 50% deviance from 1-1-3-1-1 pattern
|
||||||
|
return
|
||||||
|
Math.abs(moduleSize - stateCount[0]) << 1 <= moduleSize &&
|
||||||
|
Math.abs(moduleSize - stateCount[1]) << 1 <= moduleSize &&
|
||||||
|
Math.abs(3 * moduleSize - stateCount[2]) << 1 <= 3 * moduleSize &&
|
||||||
|
Math.abs(moduleSize - stateCount[3]) << 1 <= moduleSize &&
|
||||||
|
Math.abs(moduleSize - stateCount[4]) << 1 <= moduleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float crossCheckVertical(int startI, int centerJ, int maxCount) {
|
||||||
|
MonochromeBitmapSource image = this.image;
|
||||||
|
|
||||||
|
int maxI = image.getHeight();
|
||||||
|
int[] stateCount = new int[5];
|
||||||
|
|
||||||
|
int i = startI;
|
||||||
|
while (i >= 0 && image.isBlack(centerJ, i)) {
|
||||||
|
stateCount[2]++;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (i < 0) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (i >= 0 && !image.isBlack(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 Float.NaN;
|
||||||
|
}
|
||||||
|
while (i >= 0 && image.isBlack(centerJ, i) && stateCount[0] <= maxCount) {
|
||||||
|
stateCount[0]++;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (i < 0 || stateCount[0] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = startI + 1;
|
||||||
|
while (i < maxI && image.isBlack(centerJ, i)) {
|
||||||
|
stateCount[2]++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i == maxI) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (i < maxI && !image.isBlack(centerJ, i) && stateCount[3] < maxCount) {
|
||||||
|
stateCount[3]++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i == maxI || stateCount[3] >= maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (i < maxI && image.isBlack(centerJ, i) && stateCount[4] < maxCount) {
|
||||||
|
stateCount[4]++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (stateCount[4] >= maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float crossCheckHorizontal(int startJ, int centerI, int maxCount) {
|
||||||
|
MonochromeBitmapSource image = this.image;
|
||||||
|
|
||||||
|
int maxJ = image.getWidth();
|
||||||
|
int[] stateCount = new int[5];
|
||||||
|
|
||||||
|
int j = startJ;
|
||||||
|
while (j >= 0 && image.isBlack(j, centerI)) {
|
||||||
|
stateCount[2]++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
if (j < 0) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (j >= 0 && !image.isBlack(j, centerI) && stateCount[1] <= maxCount) {
|
||||||
|
stateCount[1]++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
// If already too many modules in this state or ran off the edge:
|
||||||
|
if (j < 0 || stateCount[1] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (j >= 0 && image.isBlack(j, centerI) && stateCount[0] <= maxCount) {
|
||||||
|
stateCount[0]++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
if (j < 0 || stateCount[0] > maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
j = startJ + 1;
|
||||||
|
while (j < maxJ && image.isBlack(j, centerI)) {
|
||||||
|
stateCount[2]++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j == maxJ) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (j < maxJ && !image.isBlack(j, centerI) && stateCount[3] < maxCount) {
|
||||||
|
stateCount[3]++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j == maxJ || stateCount[3] >= maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
while (j < maxJ && image.isBlack(j, centerI) && stateCount[4] < maxCount) {
|
||||||
|
stateCount[4]++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (stateCount[4] >= maxCount) {
|
||||||
|
return Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handlePossibleCenter(int[] stateCount,
|
||||||
|
int i,
|
||||||
|
int j) {
|
||||||
|
float centerJ = centerFromEnd(stateCount, j);
|
||||||
|
float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2]);
|
||||||
|
if (!Float.isNaN(centerI)) {
|
||||||
|
// Re-cross check
|
||||||
|
centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2]);
|
||||||
|
if (!Float.isNaN(centerJ)) {
|
||||||
|
float estimatedModuleSize = (float) (stateCount[0] +
|
||||||
|
stateCount[1] +
|
||||||
|
stateCount[2] +
|
||||||
|
stateCount[3] +
|
||||||
|
stateCount[4]) / 7.0f;
|
||||||
|
boolean found = false;
|
||||||
|
int max = possibleCenters.size();
|
||||||
|
for (int index = 0; index < max; index++) {
|
||||||
|
FinderPattern center = (FinderPattern) possibleCenters.elementAt(index);
|
||||||
|
// Look for about the same center and module size:
|
||||||
|
if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
|
||||||
|
center.incrementCount();
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
possibleCenters.addElement(
|
||||||
|
new FinderPattern(centerJ, centerI, estimatedModuleSize));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findRowSkip() {
|
||||||
|
int max = possibleCenters.size();
|
||||||
|
if (max <= 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
FinderPattern firstConfirmedCenter = null;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
FinderPattern center = (FinderPattern) possibleCenters.elementAt(i);
|
||||||
|
if (center.getCount() >= 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 first. Draw it out.
|
||||||
|
hasSkipped = true;
|
||||||
|
return (int) Math.abs(Math.abs(firstConfirmedCenter.getX() - center.getX()) -
|
||||||
|
Math.abs(firstConfirmedCenter.getY() - center.getY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean haveMulitplyConfirmedCenters() {
|
||||||
|
int count = 0;
|
||||||
|
int max = possibleCenters.size();
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
if (((FinderPattern) possibleCenters.elementAt(i)).getCount() >= CENTER_QUORUM) {
|
||||||
|
if (++count == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FinderPattern[] selectBestPatterns() throws ReaderException {
|
||||||
|
Collections.insertionSort(possibleCenters, new CenterComparator());
|
||||||
|
int size = 0;
|
||||||
|
int max = possibleCenters.size();
|
||||||
|
while (size < max) {
|
||||||
|
if (((FinderPattern) possibleCenters.elementAt(size)).getCount() < CENTER_QUORUM) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 3) {
|
||||||
|
// Couldn't find enough finder patterns
|
||||||
|
throw new ReaderException("Could not find three finder patterns");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 3) {
|
||||||
|
// Found just enough -- hope these are good!
|
||||||
|
// toArray() is not available
|
||||||
|
FinderPattern[] result = new FinderPattern[possibleCenters.size()];
|
||||||
|
for (int i = 0; i < possibleCenters.size(); i++) {
|
||||||
|
result[i] = (FinderPattern) possibleCenters.elementAt(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleCenters.setSize(size);
|
||||||
|
|
||||||
|
// Hmm, multiple found. We need to pick the best three. Find the most
|
||||||
|
// popular ones whose module size is nearest the average
|
||||||
|
|
||||||
|
float averageModuleSize = 0.0f;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
averageModuleSize += ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
|
||||||
|
}
|
||||||
|
averageModuleSize /= (float) size;
|
||||||
|
|
||||||
|
Collections.insertionSort(
|
||||||
|
possibleCenters,
|
||||||
|
new ClosestToAverageComparator(averageModuleSize));
|
||||||
|
|
||||||
|
//return confirmedCenters.subList(0, 3).toArray(new FinderPattern[3]);
|
||||||
|
FinderPattern[] result = new FinderPattern[3];
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
result[i] = (FinderPattern) possibleCenters.elementAt(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FinderPattern[] orderBestPatterns(FinderPattern[] patterns) {
|
||||||
|
|
||||||
|
// Find distances between pattern centers
|
||||||
|
float abDistance = distance(patterns[0], patterns[1]);
|
||||||
|
float bcDistance = distance(patterns[1], patterns[2]);
|
||||||
|
float acDistance = distance(patterns[0], patterns[2]);
|
||||||
|
|
||||||
|
FinderPattern topLeft;
|
||||||
|
FinderPattern topRight;
|
||||||
|
FinderPattern bottomLeft;
|
||||||
|
// Assume one closest to other two is top left
|
||||||
|
if (bcDistance >= abDistance && bcDistance >= acDistance) {
|
||||||
|
topLeft = patterns[0];
|
||||||
|
topRight = patterns[1]; // These two are guesses at the moment
|
||||||
|
bottomLeft = patterns[2];
|
||||||
|
} else if (acDistance >= bcDistance && acDistance >= abDistance) {
|
||||||
|
topLeft = patterns[1];
|
||||||
|
topRight = patterns[0];
|
||||||
|
bottomLeft = patterns[2];
|
||||||
|
} else {
|
||||||
|
topLeft = patterns[2];
|
||||||
|
topRight = patterns[0];
|
||||||
|
bottomLeft = patterns[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use cross product to figure out which of other1/2 is the bottom left
|
||||||
|
// pattern. The vector "top-left -> bottom-left" x "top-left -> top-right"
|
||||||
|
// should yield a vector with positive z component
|
||||||
|
if ((bottomLeft.getY() - topLeft.getY()) * (topRight.getX() - topLeft.getX()) <
|
||||||
|
(bottomLeft.getX() - topLeft.getX()) * (topRight.getY() - topLeft.getY())) {
|
||||||
|
FinderPattern temp = topRight;
|
||||||
|
topRight = bottomLeft;
|
||||||
|
bottomLeft = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FinderPattern[]{bottomLeft, topLeft, topRight};
|
||||||
|
}
|
||||||
|
|
||||||
|
static float distance(ResultPoint pattern1, ResultPoint pattern2) {
|
||||||
|
float xDiff = pattern1.getX() - pattern2.getX();
|
||||||
|
float yDiff = pattern1.getY() - pattern2.getY();
|
||||||
|
return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CenterComparator implements Comparator {
|
||||||
|
public int compare(Object center1, Object center2) {
|
||||||
|
return ((FinderPattern) center2).getCount() - ((FinderPattern) center1).getCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClosestToAverageComparator implements Comparator {
|
||||||
|
private float averageModuleSize;
|
||||||
|
|
||||||
|
private ClosestToAverageComparator(float averageModuleSize) {
|
||||||
|
this.averageModuleSize = averageModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(Object center1, Object center2) {
|
||||||
|
return
|
||||||
|
Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - averageModuleSize) <
|
||||||
|
Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - averageModuleSize) ?
|
||||||
|
-1 :
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
final class FinderPatternInfo {
|
||||||
|
|
||||||
|
private final float rawEstimatedModuleSize;
|
||||||
|
private final FinderPattern bottomLeft;
|
||||||
|
private final FinderPattern topLeft;
|
||||||
|
private final FinderPattern topRight;
|
||||||
|
|
||||||
|
FinderPatternInfo(float rawEstimatedModuleSize,
|
||||||
|
FinderPattern[] patternCenters) {
|
||||||
|
this.rawEstimatedModuleSize = rawEstimatedModuleSize;
|
||||||
|
this.bottomLeft = patternCenters[0];
|
||||||
|
this.topLeft = patternCenters[1];
|
||||||
|
this.topRight = patternCenters[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRawEstimatedModuleSize() {
|
||||||
|
return rawEstimatedModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
FinderPattern getBottomLeft() {
|
||||||
|
return bottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
FinderPattern getTopLeft() {
|
||||||
|
return topLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
FinderPattern getTopRight() {
|
||||||
|
return topRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
88
core/src/com/google/zxing/qrcode/detector/GridSampler.java
Normal file
88
core/src/com/google/zxing/qrcode/detector/GridSampler.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this class can, given locations of finder patterns for a QR code in an
|
||||||
|
* image, sample the right points in the image to reconstruct the QR code, accounting for
|
||||||
|
* perspective distortion. It is abstracted since it is relatively expensive and should be allowed
|
||||||
|
* to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
|
||||||
|
* Imaging library, but which may not be available in other environments such as J2ME, and vice
|
||||||
|
* versa.
|
||||||
|
*
|
||||||
|
* The implementation used can be controlled by calling {@link #setGridSamplerClassName(String)}
|
||||||
|
* with the name of a class which implements this interface.
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public abstract class GridSampler {
|
||||||
|
|
||||||
|
private static final String DEFAULT_IMPL_CLASS =
|
||||||
|
"com.google.zxing.qrcode.detector.DefaultGridSampler";
|
||||||
|
|
||||||
|
private static String gridSamplerClassName = DEFAULT_IMPL_CLASS;
|
||||||
|
private static GridSampler gridSampler;
|
||||||
|
|
||||||
|
public static void setGridSamplerClassName(String className) {
|
||||||
|
if (className == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
gridSamplerClassName = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GridSampler getInstance() {
|
||||||
|
if (gridSampler == null) {
|
||||||
|
try {
|
||||||
|
Class gridSamplerClass = Class.forName(gridSamplerClassName);
|
||||||
|
gridSampler = (GridSampler) gridSamplerClass.newInstance();
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
throw new RuntimeException(cnfe.toString());
|
||||||
|
} catch (IllegalAccessException iae) {
|
||||||
|
throw new RuntimeException(iae.toString());
|
||||||
|
} catch (InstantiationException ie) {
|
||||||
|
throw new RuntimeException(ie.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gridSampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BitMatrix sampleGrid(MonochromeBitmapSource image,
|
||||||
|
FinderPattern topLeft,
|
||||||
|
FinderPattern topRight,
|
||||||
|
FinderPattern bottomLeft,
|
||||||
|
AlignmentPattern alignmentPattern,
|
||||||
|
int dimension) throws ReaderException;
|
||||||
|
|
||||||
|
protected static void checkEndpoint(MonochromeBitmapSource image, float[] points)
|
||||||
|
throws ReaderException {
|
||||||
|
int x = (int) points[0];
|
||||||
|
int y = (int) points[1];
|
||||||
|
if (x < 0 || x >= image.getWidth() || y < 0 || y >= image.getHeight()) {
|
||||||
|
throw new ReaderException("Transformed point out of bounds at " + x + ',' + y);
|
||||||
|
}
|
||||||
|
x = (int) points[points.length - 2];
|
||||||
|
y = (int) points[points.length - 1];
|
||||||
|
if (x < 0 || x >= image.getWidth() || y < 0 || y >= image.getHeight()) {
|
||||||
|
throw new ReaderException("Transformed point out of bounds at " + x + ',' + y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.google.zxing.qrcode.detector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO need to reimplement this from scratch. This is derived from jai-core from Sun
|
||||||
|
* and it is not clear we can redistribute this modification.
|
||||||
|
*/
|
||||||
|
final class JAIPerspectiveTransform {
|
||||||
|
|
||||||
|
private float m00, m01, m02, m10, m11, m12, m20, m21, m22;
|
||||||
|
|
||||||
|
JAIPerspectiveTransform() {
|
||||||
|
m00 = m11 = m22 = 1.0f;
|
||||||
|
m01 = m02 = m10 = m12 = m20 = m21 = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeAdjoint() {
|
||||||
|
float m00p = m11 * m22 - m12 * m21;
|
||||||
|
float m01p = m12 * m20 - m10 * m22; // flipped sign
|
||||||
|
float m02p = m10 * m21 - m11 * m20;
|
||||||
|
float m10p = m02 * m21 - m01 * m22; // flipped sign
|
||||||
|
float m11p = m00 * m22 - m02 * m20;
|
||||||
|
float m12p = m01 * m20 - m00 * m21; // flipped sign
|
||||||
|
float m20p = m01 * m12 - m02 * m11;
|
||||||
|
float m21p = m02 * m10 - m00 * m12; // flipped sign
|
||||||
|
float m22p = m00 * m11 - m01 * m10;
|
||||||
|
// Transpose and copy sub-determinants
|
||||||
|
m00 = m00p;
|
||||||
|
m01 = m10p;
|
||||||
|
m02 = m20p;
|
||||||
|
m10 = m01p;
|
||||||
|
m11 = m11p;
|
||||||
|
m12 = m21p;
|
||||||
|
m20 = m02p;
|
||||||
|
m21 = m12p;
|
||||||
|
m22 = m22p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getSquareToQuad(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3,
|
||||||
|
JAIPerspectiveTransform tx) {
|
||||||
|
float dx3 = x0 - x1 + x2 - x3;
|
||||||
|
float dy3 = y0 - y1 + y2 - y3;
|
||||||
|
tx.m22 = 1.0f;
|
||||||
|
if ((dx3 == 0.0f) && (dy3 == 0.0f)) { // to do: use tolerance
|
||||||
|
tx.m00 = x1 - x0;
|
||||||
|
tx.m01 = x2 - x1;
|
||||||
|
tx.m02 = x0;
|
||||||
|
tx.m10 = y1 - y0;
|
||||||
|
tx.m11 = y2 - y1;
|
||||||
|
tx.m12 = y0;
|
||||||
|
tx.m20 = 0.0f;
|
||||||
|
tx.m21 = 0.0f;
|
||||||
|
} else {
|
||||||
|
float dx1 = x1 - x2;
|
||||||
|
float dy1 = y1 - y2;
|
||||||
|
float dx2 = x3 - x2;
|
||||||
|
float dy2 = y3 - y2;
|
||||||
|
float invdet = 1.0f / (dx1 * dy2 - dx2 * dy1);
|
||||||
|
tx.m20 = (dx3 * dy2 - dx2 * dy3) * invdet;
|
||||||
|
tx.m21 = (dx1 * dy3 - dx3 * dy1) * invdet;
|
||||||
|
tx.m00 = x1 - x0 + tx.m20 * x1;
|
||||||
|
tx.m01 = x3 - x0 + tx.m21 * x3;
|
||||||
|
tx.m02 = x0;
|
||||||
|
tx.m10 = y1 - y0 + tx.m20 * y1;
|
||||||
|
tx.m11 = y3 - y0 + tx.m21 * y3;
|
||||||
|
tx.m12 = y0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JAIPerspectiveTransform getSquareToQuad(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3) {
|
||||||
|
JAIPerspectiveTransform tx = new JAIPerspectiveTransform();
|
||||||
|
getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JAIPerspectiveTransform getQuadToSquare(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3) {
|
||||||
|
JAIPerspectiveTransform tx = new JAIPerspectiveTransform();
|
||||||
|
getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
|
||||||
|
tx.makeAdjoint();
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JAIPerspectiveTransform getQuadToQuad(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3,
|
||||||
|
float x0p, float y0p,
|
||||||
|
float x1p, float y1p,
|
||||||
|
float x2p, float y2p,
|
||||||
|
float x3p, float y3p) {
|
||||||
|
JAIPerspectiveTransform tx1 = getQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
|
||||||
|
JAIPerspectiveTransform tx2 = getSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
||||||
|
tx1.concatenate(tx2);
|
||||||
|
return tx1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void concatenate(JAIPerspectiveTransform Tx) {
|
||||||
|
float m00p = m00 * Tx.m00 + m10 * Tx.m01 + m20 * Tx.m02;
|
||||||
|
float m10p = m00 * Tx.m10 + m10 * Tx.m11 + m20 * Tx.m12;
|
||||||
|
float m20p = m00 * Tx.m20 + m10 * Tx.m21 + m20 * Tx.m22;
|
||||||
|
float m01p = m01 * Tx.m00 + m11 * Tx.m01 + m21 * Tx.m02;
|
||||||
|
float m11p = m01 * Tx.m10 + m11 * Tx.m11 + m21 * Tx.m12;
|
||||||
|
float m21p = m01 * Tx.m20 + m11 * Tx.m21 + m21 * Tx.m22;
|
||||||
|
float m02p = m02 * Tx.m00 + m12 * Tx.m01 + m22 * Tx.m02;
|
||||||
|
float m12p = m02 * Tx.m10 + m12 * Tx.m11 + m22 * Tx.m12;
|
||||||
|
float m22p = m02 * Tx.m20 + m12 * Tx.m21 + m22 * Tx.m22;
|
||||||
|
m00 = m00p;
|
||||||
|
m10 = m10p;
|
||||||
|
m20 = m20p;
|
||||||
|
m01 = m01p;
|
||||||
|
m11 = m11p;
|
||||||
|
m21 = m21p;
|
||||||
|
m02 = m02p;
|
||||||
|
m12 = m12p;
|
||||||
|
m22 = m22p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transform(float[] points) {
|
||||||
|
int max = points.length;
|
||||||
|
for (int offset = 0; offset < max; offset += 2) {
|
||||||
|
float x = points[offset];
|
||||||
|
float y = points[offset + 1];
|
||||||
|
float w = m20 * x + m21 * y + m22;
|
||||||
|
if (w == 0.0f) {
|
||||||
|
points[offset] = x;
|
||||||
|
points[offset + 1] = y;
|
||||||
|
} else {
|
||||||
|
float oneOverW = 1.0f / w;
|
||||||
|
points[offset] = (m00 * x + m01 * y + m02) * oneOverW;
|
||||||
|
points[offset + 1] = (m10 * x + m11 * y + m12) * oneOverW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.common.BitMatrix;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class DataMaskTestCase extends TestCase {
|
||||||
|
|
||||||
|
public void testMask0() {
|
||||||
|
testMaskAcrossDimensions(0, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return (i + j) % 2 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask1() {
|
||||||
|
testMaskAcrossDimensions(1, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return i % 2 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask2() {
|
||||||
|
testMaskAcrossDimensions(2, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return j % 3 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask3() {
|
||||||
|
testMaskAcrossDimensions(3, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return (i + j) % 3 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask4() {
|
||||||
|
testMaskAcrossDimensions(4, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return (i/2 + j/3) % 2 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask5() {
|
||||||
|
testMaskAcrossDimensions(5, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return (i*j) % 2 + (i*j) % 3 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask6() {
|
||||||
|
testMaskAcrossDimensions(6, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return ((i*j) % 2 + (i*j) % 3) % 2 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMask7() {
|
||||||
|
testMaskAcrossDimensions(7, new MaskCondition() {
|
||||||
|
public boolean isMasked(int i, int j) {
|
||||||
|
return ((i+j) % 2 + (i*j) % 3) % 2 == 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMaskAcrossDimensions(int reference,
|
||||||
|
MaskCondition condition) {
|
||||||
|
DataMask mask = DataMask.forReference(reference);
|
||||||
|
for (int version = 1; version <= 40; version++) {
|
||||||
|
int dimension = 17 + 4*version;
|
||||||
|
testMask(mask, dimension, condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMask(DataMask mask, int dimension, MaskCondition condition) {
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
mask.unmaskBitMatrix(bits.getBits(), dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
assertEquals(
|
||||||
|
"(" + i + ',' + j + ')',
|
||||||
|
condition.isMasked(i, j),
|
||||||
|
bits.get(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static interface MaskCondition {
|
||||||
|
boolean isMasked(int i, int j);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestRunner.run(new DataMaskTestCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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 junit.framework.TestCase;
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class FormatInformationTestCase extends TestCase {
|
||||||
|
|
||||||
|
public void testBitsDiffering() {
|
||||||
|
assertEquals(0, FormatInformation.numBitsDiffering(1, 1));
|
||||||
|
assertEquals(1, FormatInformation.numBitsDiffering(0, 2));
|
||||||
|
assertEquals(2, FormatInformation.numBitsDiffering(1, 2));
|
||||||
|
assertEquals(32, FormatInformation.numBitsDiffering(-1,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDecode() {
|
||||||
|
// Normal case
|
||||||
|
FormatInformation expected =
|
||||||
|
FormatInformation.decodeFormatInformation(0x2BED ^ 0x5412);
|
||||||
|
assertEquals((byte) 0x07, expected.getDataMask());
|
||||||
|
assertEquals(ErrorCorrectionLevel.Q, expected.getErrorCorrectionLevel());
|
||||||
|
// where the code forgot the mask!
|
||||||
|
assertEquals(expected, FormatInformation.decodeFormatInformation(0x2BED));
|
||||||
|
// 1,2,3,4 bits difference
|
||||||
|
assertEquals(expected, FormatInformation.decodeFormatInformation(
|
||||||
|
0x2BEF ^ 0x5412));
|
||||||
|
assertEquals(expected, FormatInformation.decodeFormatInformation(
|
||||||
|
0x2BEE ^ 0x5412));
|
||||||
|
assertEquals(expected, FormatInformation.decodeFormatInformation(
|
||||||
|
0x2BEA ^ 0x5412));
|
||||||
|
assertNull(FormatInformation.decodeFormatInformation(0x2BE2 ^ 0x5412));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestRunner.run(new DataMaskTestCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
javame/build.xml
Normal file
46
javame/build.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="javame" default="build">
|
||||||
|
|
||||||
|
<property name="WTK-home" value="/usr/local/WTK2.5.2"/>
|
||||||
|
<property name="JDK1.4-home" value="/usr/lib/jvm/j2sdk1.4.2_16"/>
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<tstamp/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="init">
|
||||||
|
<mkdir dir="build"/>
|
||||||
|
<javac srcdir="src"
|
||||||
|
destdir="build"
|
||||||
|
source="1.4"
|
||||||
|
target="1.4"
|
||||||
|
bootclasspath="${JDK1.4-home}/jre/lib/rt.jar"
|
||||||
|
optimize="true"
|
||||||
|
debug="true"
|
||||||
|
deprecation="true"
|
||||||
|
fork="true">
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="${WTK-home}/lib/cldcapi11.jar"/>
|
||||||
|
<pathelement location="${WTK-home}/lib/midpapi20.jar"/>
|
||||||
|
<pathelement location="${WTK-home}/lib/mmapi.jar"/>
|
||||||
|
<pathelement location="../core/core.jar"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
|
||||||
|
<mkdir dir="build-j2me"/>
|
||||||
|
<exec executable="${WTK-home}/bin/preverify">
|
||||||
|
<arg line="-classpath ${WTK-home}/lib/cldcapi11.jar:${WTK-home}/lib/midpapi20.jar:${WTK-home}/lib/mmapi.jar:${WTK-home}/lib/satsa-apdu.jar:../core/core.jar -d build-j2me build"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<!-- TODO add manifest -->
|
||||||
|
<jar jarfile="javame.jar" basedir="build-j2me"/>
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="build"/>
|
||||||
|
<delete dir="build-j2me"/>
|
||||||
|
<delete file="javame.jar"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.j2me;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
import com.google.zxing.common.BlackPointEstimator;
|
||||||
|
|
||||||
|
import javax.microedition.lcdui.Image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen (srowen@google.com)
|
||||||
|
*/
|
||||||
|
final class LCDUIImageMonochromeBitmapSource implements MonochromeBitmapSource {
|
||||||
|
|
||||||
|
private final int[] rgbPixels;
|
||||||
|
private final int blackPoint;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
|
||||||
|
LCDUIImageMonochromeBitmapSource(final Image image) {
|
||||||
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
int[] rgbPixels = new int[width * height];
|
||||||
|
this.rgbPixels = rgbPixels;
|
||||||
|
image.getRGB(rgbPixels, 0, width, 0, 0, width, height);
|
||||||
|
int[] luminanceBuckets = new int[32];
|
||||||
|
int minDimension = width < height ? width : height;
|
||||||
|
for (int n = 0, offset = 0; n < minDimension; n++, offset += width + 1) {
|
||||||
|
luminanceBuckets[computeRGBLuminance(rgbPixels[offset]) >> 3]++;
|
||||||
|
}
|
||||||
|
blackPoint = BlackPointEstimator.estimate(luminanceBuckets) << 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlack(int x, int y) {
|
||||||
|
return computeRGBLuminance(rgbPixels[x + y * width]) < blackPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {
|
||||||
|
if (row == null) {
|
||||||
|
row = new BitArray(getWidth);
|
||||||
|
} else {
|
||||||
|
row.clear();
|
||||||
|
}
|
||||||
|
for (int i = 0, offset = y * width + startX; i < getWidth; i++, offset++) {
|
||||||
|
if (computeRGBLuminance(rgbPixels[offset]) < blackPoint) {
|
||||||
|
row.set(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts luminance from a pixel from this source. By default, the source is assumed to use RGB,
|
||||||
|
* so this implementation computes luminance is a function of a red, green and blue components as
|
||||||
|
* follows:
|
||||||
|
*
|
||||||
|
* <code>Y = 0.299R + 0.587G + 0.114B</code>
|
||||||
|
*
|
||||||
|
* where R, G, and B are values in [0,1].
|
||||||
|
*/
|
||||||
|
private static int computeRGBLuminance(int pixel) {
|
||||||
|
// Coefficients add up to 1024 to make the divide into a fast shift
|
||||||
|
return (306 * ((pixel >> 16) & 0xFF) +
|
||||||
|
601 * ((pixel >> 8) & 0xFF) +
|
||||||
|
117 * (pixel & 0xFF)) >> 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
180
javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java
Normal file
180
javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.j2me;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.MultiFormatReader;
|
||||||
|
import com.google.zxing.Reader;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.qrcode.detector.GridSampler;
|
||||||
|
|
||||||
|
import javax.microedition.lcdui.Alert;
|
||||||
|
import javax.microedition.lcdui.AlertType;
|
||||||
|
import javax.microedition.lcdui.Canvas;
|
||||||
|
import javax.microedition.lcdui.Command;
|
||||||
|
import javax.microedition.lcdui.CommandListener;
|
||||||
|
import javax.microedition.lcdui.Display;
|
||||||
|
import javax.microedition.lcdui.Displayable;
|
||||||
|
import javax.microedition.lcdui.Graphics;
|
||||||
|
import javax.microedition.lcdui.Image;
|
||||||
|
import javax.microedition.media.Manager;
|
||||||
|
import javax.microedition.media.MediaException;
|
||||||
|
import javax.microedition.media.Player;
|
||||||
|
import javax.microedition.media.control.VideoControl;
|
||||||
|
import javax.microedition.midlet.MIDlet;
|
||||||
|
import javax.microedition.midlet.MIDletStateChangeException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen (srowen@google.com)
|
||||||
|
*/
|
||||||
|
public final class ZXingMIDlet extends MIDlet implements CommandListener {
|
||||||
|
|
||||||
|
private static final Command DECODE = new Command("Decode", Command.SCREEN, 1);
|
||||||
|
private static final Command EXIT = new Command("Exit", Command.EXIT, 1);
|
||||||
|
|
||||||
|
static {
|
||||||
|
GridSampler.setGridSamplerClassName("com.google.zxing.client.j2me.JAIGridSampler");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Player player;
|
||||||
|
private VideoControl videoControl;
|
||||||
|
|
||||||
|
protected void startApp() throws MIDletStateChangeException {
|
||||||
|
try {
|
||||||
|
player = Manager.createPlayer("capture://video");
|
||||||
|
player.realize();
|
||||||
|
videoControl = (VideoControl) player.getControl("VideoControl");
|
||||||
|
Displayable canvas = new VideoCanvas();
|
||||||
|
videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, canvas);
|
||||||
|
videoControl.setDisplayLocation(0, 0);
|
||||||
|
videoControl.setDisplaySize(canvas.getWidth(), canvas.getHeight());
|
||||||
|
videoControl.setVisible(true);
|
||||||
|
/*
|
||||||
|
FormatControl imageFormatControl = (FormatControl)
|
||||||
|
player.getControl("javax.microedition.amms.control.ImageFormatControl");
|
||||||
|
if (imageFormatControl != null) {
|
||||||
|
imageFormatControl.setFormat("image/png");
|
||||||
|
imageFormatControl.setParameter(FormatControl.PARAM_VERSION_TYPE, "PNG");
|
||||||
|
} else {
|
||||||
|
System.out.println("ImageFormatControl not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusControl focusControl = (FocusControl)
|
||||||
|
player.getControl("javax.microedition.amms.control.FocusControl");
|
||||||
|
if (focusControl != null) {
|
||||||
|
if (focusControl.isAutoFocusSupported()) {
|
||||||
|
focusControl.setFocus(FocusControl.AUTO);
|
||||||
|
}
|
||||||
|
if (focusControl.isMacroSupported()) {
|
||||||
|
focusControl.setMacro(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("FocusControl not supported");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
canvas.addCommand(DECODE);
|
||||||
|
canvas.addCommand(EXIT);
|
||||||
|
canvas.setCommandListener(this);
|
||||||
|
Display.getDisplay(this).setCurrent(canvas);
|
||||||
|
player.start();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new MIDletStateChangeException(ioe.toString());
|
||||||
|
} catch (MediaException me) {
|
||||||
|
throw new MIDletStateChangeException(me.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commandAction(Command command, Displayable displayable) {
|
||||||
|
if (command.equals(DECODE)) {
|
||||||
|
new SnapshotThread().start();
|
||||||
|
} else if (command.equals(EXIT)) {
|
||||||
|
destroyApp(false);
|
||||||
|
notifyDestroyed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void pauseApp() {
|
||||||
|
if (player != null) {
|
||||||
|
try {
|
||||||
|
player.stop();
|
||||||
|
} catch (MediaException me) {
|
||||||
|
// continue?
|
||||||
|
showError(me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void destroyApp(boolean unconditional) {
|
||||||
|
if (player != null) {
|
||||||
|
player.close();
|
||||||
|
player = null;
|
||||||
|
videoControl = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(String title, String text) {
|
||||||
|
Alert alert = new Alert(title, text, null, AlertType.INFO);
|
||||||
|
alert.setTimeout(Alert.FOREVER);
|
||||||
|
showAlert(alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showError(Throwable t) {
|
||||||
|
showAlert(new Alert("Error", t.getMessage(), null, AlertType.ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(Alert alert) {
|
||||||
|
Display display = Display.getDisplay(this);
|
||||||
|
display.setCurrent(alert, display.getCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VideoCanvas extends Canvas {
|
||||||
|
protected void paint(Graphics graphics) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SnapshotThread extends Thread {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
player.stop();
|
||||||
|
byte[] snapshot = videoControl.getSnapshot(null);
|
||||||
|
Image capturedImage = Image.createImage(snapshot, 0, snapshot.length);
|
||||||
|
MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
|
||||||
|
Reader reader = new MultiFormatReader();
|
||||||
|
Result result = reader.decode(source);
|
||||||
|
showAlert("Barcode detected", result.getText());
|
||||||
|
} catch (ReaderException re) {
|
||||||
|
showError(re);
|
||||||
|
} catch (MediaException me) {
|
||||||
|
showError(me);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
showError(t);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
player.start();
|
||||||
|
} catch (MediaException me) {
|
||||||
|
// continue?
|
||||||
|
showError(me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
javase/build.xml
Normal file
30
javase/build.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="javase" default="build">
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<tstamp/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="init">
|
||||||
|
<mkdir dir="build"/>
|
||||||
|
<javac srcdir="src"
|
||||||
|
destdir="build"
|
||||||
|
source="1.5"
|
||||||
|
target="1.5"
|
||||||
|
optimize="true"
|
||||||
|
debug="true"
|
||||||
|
deprecation="true">
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="../core/core.jar"/>
|
||||||
|
<pathelement location="../core-ext/core-ext.jar"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
<jar jarfile="javase.jar" basedir="build"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="build"/>
|
||||||
|
<delete file="javase.jar"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.j2se;
|
||||||
|
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
import com.google.zxing.common.BlackPointEstimator;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An implementation based upon {@link BufferedImage}. This provides access to the
|
||||||
|
* underlying image as if it were a monochrome image. Behind the scenes, it is evaluating
|
||||||
|
* the luminance of the underlying image by retrieving its pixels' RGB values.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class BufferedImageMonochromeBitmapSource implements MonochromeBitmapSource {
|
||||||
|
|
||||||
|
private final BufferedImage image;
|
||||||
|
private final int blackPoint;
|
||||||
|
|
||||||
|
public BufferedImageMonochromeBitmapSource(BufferedImage image) {
|
||||||
|
this.image = image;
|
||||||
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
int[] luminanceBuckets = new int[32];
|
||||||
|
int minDimension = width < height ? width : height;
|
||||||
|
int startI = height == minDimension ? 0 : (height - width) >> 1;
|
||||||
|
int startJ = width == minDimension ? 0 : (width - height) >> 1;
|
||||||
|
for (int n = 0; n < minDimension; n++) {
|
||||||
|
int pixel = image.getRGB(startJ + n, startI + n);
|
||||||
|
luminanceBuckets[computeRGBLuminance(pixel) >> 3]++;
|
||||||
|
}
|
||||||
|
blackPoint = BlackPointEstimator.estimate(luminanceBuckets) << 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlack(int x, int y) {
|
||||||
|
return computeRGBLuminance(image.getRGB(x, y)) < blackPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {
|
||||||
|
if (row == null) {
|
||||||
|
row = new BitArray(getWidth);
|
||||||
|
} else {
|
||||||
|
row.clear();
|
||||||
|
}
|
||||||
|
int[] pixelRow = image.getRGB(startX, y, getWidth, 1, null, 0, getWidth);
|
||||||
|
for (int i = 0; i < getWidth; i++) {
|
||||||
|
if (computeRGBLuminance(pixelRow[i]) < blackPoint) {
|
||||||
|
row.set(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return image.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return image.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts luminance from a pixel from this source. By default, the source is assumed to use RGB,
|
||||||
|
* so this implementation computes luminance is a function of a red, green and blue components as
|
||||||
|
* follows:
|
||||||
|
*
|
||||||
|
* <code>Y = 0.299R + 0.587G + 0.114B</code>
|
||||||
|
*
|
||||||
|
* where R, G, and B are values in [0,1].
|
||||||
|
*/
|
||||||
|
private static int computeRGBLuminance(int pixel) {
|
||||||
|
// Coefficients add up to 1024 to make the divide into a fast shift
|
||||||
|
return (306 * ((pixel >> 16) & 0xFF) +
|
||||||
|
601 * ((pixel >> 8) & 0xFF) +
|
||||||
|
117 * (pixel & 0xFF)) >> 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.client.j2se;
|
||||||
|
|
||||||
|
import com.google.zxing.MultiFormatReader;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Simply attempts to decode the barcode in the image indicated by the single argument
|
||||||
|
* to this program, which may be file or a URI. The raw text is printed.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen), dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class CommandLineRunner {
|
||||||
|
|
||||||
|
private CommandLineRunner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
File inputFile = new File(args[0]);
|
||||||
|
if (inputFile.exists()) {
|
||||||
|
if (inputFile.isDirectory()) {
|
||||||
|
for (File input : inputFile.listFiles()) {
|
||||||
|
decode(input.toURI());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decode(inputFile.toURI());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decode(new URI(args[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decode(URI uri) throws IOException, ReaderException {
|
||||||
|
BufferedImage image = ImageIO.read(uri.toURL());
|
||||||
|
String result =
|
||||||
|
new MultiFormatReader().decode(new BufferedImageMonochromeBitmapSource(image)).getText();
|
||||||
|
System.out.println(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
lib/junit-LICENSE.html
Normal file
125
lib/junit-LICENSE.html
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>Common Public License - v 1.0</TITLE>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||||
|
</HEAD>
|
||||||
|
|
||||||
|
<BODY BGCOLOR="#FFFFFF" VLINK="#800000">
|
||||||
|
|
||||||
|
|
||||||
|
<P ALIGN="CENTER"><B>Common Public License - v 1.0</B>
|
||||||
|
<P><B></B><FONT SIZE="3"></FONT>
|
||||||
|
<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"><B>1. DEFINITIONS</B></FONT>
|
||||||
|
<P><FONT SIZE="2">"Contribution" means:</FONT>
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT">
|
||||||
|
b) in the case of each subsequent Contributor:</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">i) changes to the Program, and</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">ii) additions to the Program;</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. </FONT><FONT SIZE="2">Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. </FONT></UL>
|
||||||
|
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. </FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT>
|
||||||
|
<P><FONT SIZE="2"><B></B></FONT>
|
||||||
|
<P><FONT SIZE="2"><B>2. GRANT OF RIGHTS</B></FONT>
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a) </FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. </FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2"></FONT></UL>
|
||||||
|
|
||||||
|
<P><FONT SIZE="2"><B>3. REQUIREMENTS</B></FONT>
|
||||||
|
<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT>
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">a) it complies with the terms and conditions of this Agreement; and</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">b) its license agreement:</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">i) effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2"> states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL>
|
||||||
|
|
||||||
|
<P><FONT SIZE="2">When the Program is made available in source code form:</FONT>
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">a) it must be made available under this Agreement; and </FONT></UL>
|
||||||
|
|
||||||
|
|
||||||
|
<UL><FONT SIZE="2">b) a copy of this Agreement must be included with each copy of the Program. </FONT></UL>
|
||||||
|
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT>
|
||||||
|
<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program. </FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. </FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"><B>4. COMMERCIAL DISTRIBUTION</B></FONT>
|
||||||
|
<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT>
|
||||||
|
<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5. NO WARRANTY</B></FONT>
|
||||||
|
<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">. </FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6. DISCLAIMER OF LIABILITY</B></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"><B>7. GENERAL</B></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. </FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version. </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2"> All rights in the Program not expressly granted under this Agreement are reserved.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.</FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
|
||||||
|
<P><FONT SIZE="2"></FONT>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
|
||||||
|
</HTML>
|
BIN
lib/junit.jar
Normal file
BIN
lib/junit.jar
Normal file
Binary file not shown.
Loading…
Reference in a new issue