mirror of https://github.com/NekoX-Dev/NekoX.git
(? ... !~
This commit is contained in:
parent
e68969e2b9
commit
8be981e4a0
|
@ -0,0 +1,25 @@
|
|||
* text=auto
|
||||
|
||||
*.txt text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.json text eol=lf
|
||||
*.java text eol=lf
|
||||
*.kt text eol=lf
|
||||
*.c text eol=lf
|
||||
*.h text eol=lf
|
||||
*.cpp text eol=lf
|
||||
*.py text eol=lf
|
||||
*.mk text eol=lf
|
||||
*.pro text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.conf text eol=lf
|
||||
*.awk text eol=lf
|
||||
*.sed text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
||||
*.attheme text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
|
||||
*.p12 binary
|
|
@ -1 +1 @@
|
|||
patreon: NekoWorkshop
|
||||
patreon: NekoXDev
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
name: Canary Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
canaryBuild:
|
||||
name: Canary Build
|
||||
runs-on: ubuntu-latest
|
||||
if: "!endsWith(github.event.head_commit.message, '~')"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Run Gradle Build
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
sudo bash <<EOF
|
||||
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
|
||||
./gradlew assembleMinApi21Release
|
||||
EOF
|
||||
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*arm64-v8a*.apk")
|
||||
- uses: NekogramX/nekox-build-script@master
|
||||
env:
|
||||
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
RELEASE_CHANNEL: ${{ secrets.RELEASE_CHANNEL }}
|
||||
CANARY_CHANNEL: ${{ secrets.CANARY_CHANNEL }}
|
|
@ -0,0 +1,77 @@
|
|||
name: Foss Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
fossBuild:
|
||||
name: Foss Build
|
||||
runs-on: ubuntu-latest
|
||||
if: "startsWith(github.event.head_commit.message, '!')"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
- name: Build native libraries
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
|
||||
cd TMessagesProj/libs
|
||||
|
||||
go env -w GOPATH=$HOME/go
|
||||
export GOPATH=$HOME/go
|
||||
export PATH=$PATH:$GOPATH/bin
|
||||
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go get -v golang.org/x/mobile/cmd/...
|
||||
go get -v go.starlark.net/starlark
|
||||
go get -v github.com/refraction-networking/utls
|
||||
go get -v github.com/gorilla/websocket
|
||||
go get -v -insecure v2ray.com/core
|
||||
go get github.com/2dust/AndroidLibV2rayLite
|
||||
|
||||
gomobile init
|
||||
env GO111MODULE=off gomobile bind -v -ldflags='-s -w' github.com/2dust/AndroidLibV2rayLite
|
||||
|
||||
cd ../jni
|
||||
|
||||
sudo apt-get install -y ninja-build
|
||||
export NDK=$ANDROID_HOME/ndk-bundle
|
||||
export NINJA_PATH=/usr/bin/ninja
|
||||
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
|
||||
|
||||
./build_ffmpeg_clang.sh
|
||||
./patch_ffmpeg.sh
|
||||
./patch_boringssl.sh
|
||||
./build_boringssl.sh
|
||||
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_KEY }}
|
||||
- name: assemble
|
||||
run: |
|
||||
sudo bash <<EOF
|
||||
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
|
||||
./gradlew ss-libev:assembleRelease &&
|
||||
./gradlew ssr-libev:assembleRelease &&
|
||||
rm -rf TMessagesProj/libs/*-libev-release.aar &&
|
||||
find . -name "*-libev-release.aar" -exec mv {} TMessagesProj/libs \; &&
|
||||
./gradlew assembleAfatFoss
|
||||
EOF
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: NekoX-Foss
|
||||
path: "TMessagesProj/build/outputs/apk/afat/foss"
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Native Binaries
|
||||
path: "TMessagesProj/build/intermediates/ndkBuild/afatFoss/obj/local"
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: V2ray Library
|
||||
path: "TMessagesProj/libs/libv2ray.aar"
|
|
@ -0,0 +1,77 @@
|
|||
name: Release Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
releaseBuild:
|
||||
name: Release Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Setup ghr
|
||||
run: |
|
||||
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz
|
||||
tar xvzf ghr.tar.gz; rm ghr.tar.gz
|
||||
sudo mv ghr*linux_amd64/ghr /usr/local/bin; rm -rf ghr*linux_amd64
|
||||
- name: assembleRelease
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
sudo bash <<EOF
|
||||
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
|
||||
./gradlew assembleRelease \
|
||||
assembleReleaseNoGcm \
|
||||
assembleLegacyRelease \
|
||||
assembleLegacyReleaseNoGcm
|
||||
EOF
|
||||
echo ::set-env name=AAB_FILE::$(find TMessagesProj/build/outputs/bundle -name "*.aab")
|
||||
- name: Upload apks
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GPG_KEY: ${{ secrets.GPG_KEY }}
|
||||
run: |
|
||||
mkdir apks; find . -name "*.apk" -exec mv {} apks \;
|
||||
|
||||
ghr -b "${{ github.event.head_commit.message }}" \
|
||||
-n "${{ github.ref }}" "${{ github.ref }}" apks/
|
||||
|
||||
cd apks
|
||||
|
||||
gpg --import <<EOF
|
||||
$GPG_KEY
|
||||
EOF
|
||||
|
||||
echo "DA55ACBFDDA40853BFC5496ECD109927C34A63C4" > $HOME/.gnupg/sshcontrol
|
||||
|
||||
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
|
||||
gpgconf --launch gpg-agent
|
||||
|
||||
git config --global user.name "世界"
|
||||
git config --global user.email "i@nekox.me"
|
||||
git config --global user.signingkey "DA55ACBFDDA40853BFC5496ECD109927C34A63C4"
|
||||
git config --global commit.gpgsign true
|
||||
|
||||
git init
|
||||
|
||||
git remote add github git@github.com:NekogramX/NekoX-Resources.git
|
||||
git remote add gitlab git@gitlab.com:NekohaSekai/nekox-resources.git
|
||||
git remote add gitee git@gitee.com:nekoshizuku/nekox-resources.git
|
||||
|
||||
git add . --all
|
||||
git commit -m "${{ github.event.head_commit.message }}"
|
||||
|
||||
git push github master -f
|
||||
git push gitlab master -f
|
||||
|
||||
git checkout -b release
|
||||
git push gitee release -f
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_ACCOUNT_SERVICE }}
|
||||
packageName: nekox.messenger
|
||||
releaseFile: ${{ env.AAB_FILE }}
|
||||
track: production
|
|
@ -7,3 +7,45 @@
|
|||
[submodule "TMessagesProj/jni/boringssl"]
|
||||
path = TMessagesProj/jni/boringssl
|
||||
url = https://github.com/google/boringssl
|
||||
[submodule "ss-libev/src/main/jni/shadowsocks-libev"]
|
||||
path = ss-libev/src/main/jni/shadowsocks-libev
|
||||
url = https://github.com/shadowsocks/shadowsocks-libev
|
||||
[submodule "ss-libev/src/main/jni/libancillary"]
|
||||
path = ss-libev/src/main/jni/libancillary
|
||||
url = https://github.com/shadowsocks/libancillary.git
|
||||
branch = shadowsocks-android
|
||||
[submodule "ss-libev/src/main/jni/mbedtls"]
|
||||
path = ss-libev/src/main/jni/mbedtls
|
||||
url = https://github.com/ARMmbed/mbedtls
|
||||
[submodule "ss-libev/src/main/jni/pcre"]
|
||||
path = ss-libev/src/main/jni/pcre
|
||||
url = https://android.googlesource.com/platform/external/pcre
|
||||
[submodule "ss-libev/src/main/jni/libsodium"]
|
||||
path = ss-libev/src/main/jni/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "ss-libev/src/main/jni/libev"]
|
||||
path = ss-libev/src/main/jni/libev
|
||||
url = https://git.lighttpd.net/libev.git
|
||||
[submodule "ss-libev/src/main/jni/re2"]
|
||||
path = ss-libev/src/main/jni/re2
|
||||
url = https://github.com/google/re2.git
|
||||
|
||||
[submodule "shadowsocksr-libev"]
|
||||
path = ssr-libev/src/main/jni/shadowsocks-libev
|
||||
url = https://github.com/shadowsocksRb/shadowsocksr-libev.git
|
||||
[submodule "ssr-libev/src/main/jni/libancillary"]
|
||||
path = ssr-libev/src/main/jni/libancillary
|
||||
url = https://github.com/shadowsocks/libancillary.git
|
||||
[submodule "ssr-libev/src/main/jni/mbedtls"]
|
||||
path = ssr-libev/src/main/jni/mbedtls
|
||||
url = https://github.com/ARMmbed/mbedtls
|
||||
[submodule "ssr-libev/src/main/jni/pcre"]
|
||||
path = ssr-libev/src/main/jni/pcre
|
||||
url = https://android.googlesource.com/platform/external/pcre
|
||||
[submodule "ssr-libev/src/main/jni/libsodium"]
|
||||
path = ssr-libev/src/main/jni/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
branch = stable
|
||||
[submodule "ssr-libev/src/main/jni/re2"]
|
||||
path = ssr-libev/src/main/jni/re2
|
||||
url = https://github.com/google/re2.git
|
||||
|
|
33
.travis.yml
33
.travis.yml
|
@ -1,33 +0,0 @@
|
|||
language: android
|
||||
dist: trusty
|
||||
jdk:
|
||||
- openjdk8
|
||||
android:
|
||||
components:
|
||||
- build-tools-29.0.2
|
||||
- android-29
|
||||
addons:
|
||||
apt_packages:
|
||||
- ninja-build
|
||||
|
||||
install:
|
||||
- set -e
|
||||
- echo y | sdkmanager "ndk-bundle"
|
||||
- echo y | sdkmanager "cmake;3.6.4111459"
|
||||
- echo y | sdkmanager "lldb;3.1"
|
||||
before_script:
|
||||
- export NDK=$ANDROID_HOME/ndk-bundle
|
||||
- export NINJA_PATH=/usr/bin/ninja
|
||||
- export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
|
||||
script:
|
||||
# If some stage fails, exit inmediatly
|
||||
- set -e
|
||||
# Prebuild
|
||||
- "cd TMessagesProj/jni"
|
||||
- "./build_ffmpeg_clang.sh"
|
||||
- "./patch_ffmpeg.sh"
|
||||
- "./patch_boringssl.sh"
|
||||
- "./build_boringssl.sh"
|
||||
- "cd ../.."
|
||||
# Build
|
||||
- "./gradlew assembleAfatRelease"
|
833
LICENSE
833
LICENSE
|
@ -1,281 +1,622 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
0. Definitions.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
1. Source Code.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
2. Basic Permissions.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
14. Revised Versions of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
NO WARRANTY
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms.
|
|||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
|
@ -303,37 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
|
80
README.md
80
README.md
|
@ -1,13 +1,12 @@
|
|||
# Nekogram
|
||||
![Logo](https://raw.githubusercontent.com/Nekogram/Nekogram/master/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
|
||||
Nekogram is an UNOFFICIAL app that uses Telegram's API.
|
||||
# NekoX
|
||||
![Logo](https://raw.githubusercontent.com/NekogramX/NekoX/master/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
|
||||
|
||||
- Google play store: https://play.google.com/store/apps/details?id=tw.nekomimi.nekogram
|
||||
- Update news (English): https://t.me/nekoupdates
|
||||
- Update news (Chinese): https://t.me/zuragram
|
||||
- APKs: https://github.com/Nekogram/Nekogram/releases
|
||||
- Feedback: https://t.me/nekosupportbot
|
||||
- Feedback: https://github.com/Nekogram/Nekogram/issues
|
||||
NekogramX is an UNOFFICIAL app that uses Telegram's API.
|
||||
|
||||
- Google play store: (unavailable now)
|
||||
- Update news : https://t.me/NekogramX
|
||||
- Feedback: https://t.me/NekoXChat
|
||||
- Feedback: https://github.com/NekogramX/NekoX/issues
|
||||
|
||||
## API, Protocol documentation
|
||||
|
||||
|
@ -17,26 +16,67 @@ MTproto protocol manuals: https://core.telegram.org/mtproto
|
|||
|
||||
## Compilation Guide
|
||||
|
||||
1. Download the Nekogram source code from https://github.com/Nekogram/Nekogram-FOSS ( git clone https://github.com/Nekogram/Nekogram-FOSS.git )
|
||||
2. Copy your release.keystore into TMessagesProj/config
|
||||
3. Fill out RELEASE_KEY_PASSWORD, RELEASE_KEY_ALIAS, RELEASE_STORE_PASSWORD in local.properties to access your release.keystore
|
||||
4. Open the project in the Studio (note that it should be opened, NOT imported).
|
||||
5. If you're compiling DEBUG version, make sure your build variants is set to afatDebugMultidex.
|
||||
6. You are ready to compile Nekogram.
|
||||
### Specify APP_ID and APP_HASH
|
||||
|
||||
Just fill out TELEGRAM_APP_ID and TELEGRAM_APP_HASH in local.properties
|
||||
|
||||
### Build Types
|
||||
|
||||
#### Debug
|
||||
|
||||
`./gradlew assemble<Variant>Debug`
|
||||
|
||||
The default debug key is used, and placing yours is not needed.
|
||||
|
||||
#### Release
|
||||
|
||||
`./gradlew assemble<Variant>Release`
|
||||
|
||||
The difference between release and other build types is that it adds fcm and firebase crash analysis, if you don't like them, use releaseNoGcm.
|
||||
|
||||
To compile the release version, please place your keysotre at TMessageProj/release.jks, and fill in KEYSTORE_PASS, ALIAS_NAME, ALIAS_PASS in local.properties, environment variables are also recommended
|
||||
|
||||
If you don't use NekoX's APP_ID and APP_HASH, you need to register a physical firebase app and replace google-services.json to ensure fcm works
|
||||
|
||||
#### Foss
|
||||
|
||||
`./gradlew assemble<Variant>Foss`
|
||||
|
||||
OK, a version without firebase cloud messaging and precompiled native libraries, maybe this makes you feel more free, or your phone does not have Google services.
|
||||
|
||||
To compile the foss version, please refer to [this script](.github/workflows/release.yml).
|
||||
|
||||
### Build Variants
|
||||
|
||||
Available variant list:
|
||||
|
||||
`Afat`, `Legacy`, `MinApi21`
|
||||
|
||||
|
||||
## Localization
|
||||
|
||||
Nekogram is forked from Telegram, thus most locales follows the translations of Telegram for Android, checkout https://translations.telegram.org/en/android/.
|
||||
NekoX is forked from Nekogram-FOSS, thus most locales follows the translations of Telegram for Android, checkout https://translations.telegram.org/en/android/.
|
||||
|
||||
As for the Nekogram specialized strings, we use Crowdin to translate Nekogram. Join project at https://neko.crowdin.com/nekogram. Help us bring Nekogram to the world!
|
||||
As for the specialized strings, we use Crowdin to translate Nekogram. Join project at https://neko.crowdin.com/nekogram and https://nekox.crowdin.com/nekox. Help us bring Nekogram to the world!
|
||||
|
||||
## Credits
|
||||
|
||||
<ul>
|
||||
<li>Nekogram: <a href="https://github.com/Nekogram/Nekogram/blob/master/LICENSE">GPLv2</a></li>
|
||||
<li>Telegram-FOSS: <a href="https://github.com/Telegram-FOSS-Team/Telegram-FOSS/blob/master/LICENSE">GPLv2</a></li>
|
||||
<li>v2rayNG: <a href="https://github.com/2dust/v2rayNG/blob/master/LICENSE">GPLv3</a></li>
|
||||
<li>AndroidLibV2rayLite: <a href="https://github.com/2dust/AndroidLibV2rayLite/blob/master/LICENSE">LGPLv3</a></li>
|
||||
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
|
||||
<li>shadowsocksRb-android: <a href="https://github.com/shadowsocksRb/shadowsocksRb-android/blob/master/LICENSE">GPLv3</a></li>
|
||||
</ul>
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/42698724?s=460&v=4" width="80px;"/><br /><sub>猫耳逆变器</sub>](https://github.com/NekoInverter)<br />[💻](https://github.com/Nekogram/Nekogram/commits?author=NekoInverter "Code") | [<img src="https://avatars1.githubusercontent.com/u/18373361?s=460&v=4" width="80px;"/><br /><sub>梨子</sub>](https://github.com/rikakomoe)<br />[💻](https://github.com/Nekogram/Nekogram/commits?author=rikakomoe "Code") | [<img src="https://i.loli.net/2020/01/17/e9Z5zkG7lNwUBPE.jpg" width="80px;"/><br /><sub>呆瓜</sub>](https://t.me/Duang)<br /> [🎨](#design-duang "Design") |
|
||||
| :---: | :---: | :---: |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/56506714?s=460&v=4" width="80px;"/><br /><sub>世界</sub>](https://github.com/nekohasekai)<br />[💻](https://github.com/NekogramX/NekoX/commits?author=nekohasekai "Code") | [<img src="https://avatars2.githubusercontent.com/u/42698724?s=460&v=4" width="80px;"/><br /><sub>猫耳逆变器</sub>](https://github.com/NekoInverter)<br />[💻](https://github.com/NekogramX/NekoX/commits?author=NekoInverter "Code") | [<img src="https://avatars1.githubusercontent.com/u/18373361?s=460&v=4" width="80px;"/><br /><sub>梨子</sub>](https://github.com/rikakomoe)<br />[💻](https://github.com/NekogramX/NekoX/commits?author=rikakomoe "Code") | [<img src="https://i.loli.net/2020/01/17/e9Z5zkG7lNwUBPE.jpg" width="80px;"/><br /><sub>呆瓜</sub>](https://t.me/Duang)<br /> [🎨](#design-duang "Design") |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
|
@ -1,4 +0,0 @@
|
|||
if [ "$AGENT_JOBSTATUS" == "Succeeded" ]; then
|
||||
export name=$(find $APPCENTER_OUTPUT_DIRECTORY -name '*.apk')
|
||||
curl https://api.telegram.org/bot${BOT_TOKEN}/sendDocument -X POST -F chat_id="$CHANNEL_ID" -F document="@$name"
|
||||
fi
|
|
@ -1,37 +1,47 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
configurations {
|
||||
compile.exclude module: 'support-v4'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core:1.1.0-beta01'
|
||||
|
||||
implementation 'androidx.core:core:1.3.0-alpha02'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'androidx.viewpager:viewpager:1.0.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.1.0'
|
||||
|
||||
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
||||
implementation 'com.googlecode.mp4parser:isoparser:1.0.6'
|
||||
implementation 'com.stripe:stripe-android:2.0.2'
|
||||
compileOnly 'org.checkerframework:checker-qual:3.2.0'
|
||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.5'
|
||||
implementation 'com.googlecode.mp4parser:isoparser:1.1.22'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'org.osmdroid:osmdroid-android:6.1.5'
|
||||
implementation 'org.osmdroid:osmdroid-android:6.1.6'
|
||||
implementation 'com.google.zxing:core:3.4.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71"
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
|
||||
|
||||
def okHttpVersion = '4.4.1'
|
||||
|
||||
implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
|
||||
implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:$okHttpVersion"
|
||||
|
||||
implementation files('libs/libv2ray.aar')
|
||||
|
||||
releaseImplementation 'com.google.firebase:firebase-messaging:20.1.3'
|
||||
releaseImplementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta02'
|
||||
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
//ndkVersion "20.0.5594570"
|
||||
buildToolsVersion '29.0.3'
|
||||
ndkVersion "21.0.6113669"
|
||||
|
||||
defaultConfig.applicationId = "tw.nekomimi.nekogram"
|
||||
|
||||
sourceSets.main.jniLibs.srcDirs = ['./jni/']
|
||||
defaultConfig.applicationId = "nekox.messenger"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
|
@ -45,6 +55,17 @@ android {
|
|||
disable 'BlockedPrivateApi'
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
|
||||
exclude '/fabric/**'
|
||||
exclude '/kotlin/**'
|
||||
exclude '/META-INF/*.version'
|
||||
exclude '/META-INF/*.kotlin_module'
|
||||
exclude '/builddef.lst'
|
||||
exclude '/*.txt'
|
||||
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
jumboMode = true
|
||||
}
|
||||
|
@ -54,248 +75,188 @@ android {
|
|||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
def keystorePwd = null
|
||||
def alias = null
|
||||
def pwd = null
|
||||
Properties properties;
|
||||
|
||||
if (project.rootProject.file('local.properties').exists()) {
|
||||
properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
} else {
|
||||
def base64 = System.getenv("LOCAL_PROPERTIES")
|
||||
if (base64 != null && !base64.isBlank()) {
|
||||
properties = new Properties()
|
||||
properties.load(new ByteArrayInputStream(Base64.decoder.decode(base64)))
|
||||
}
|
||||
}
|
||||
|
||||
if (properties != null) {
|
||||
keystorePwd = properties.getProperty("KEYSTORE_PASS")
|
||||
alias = properties.getProperty("ALIAS_NAME")
|
||||
pwd = properties.getProperty("ALIAS_PASS")
|
||||
}
|
||||
|
||||
keystorePwd = keystorePwd ?: System.getenv("KEYSTORE_PASS")
|
||||
alias = alias ?: System.getenv("ALIAS_NAME")
|
||||
pwd = pwd ?: System.getenv("ALIAS_PASS")
|
||||
|
||||
release {
|
||||
|
||||
storeFile project.file('release.keystore')
|
||||
storePassword keystorePwd
|
||||
keyAlias alias
|
||||
keyPassword pwd
|
||||
}
|
||||
|
||||
debug {
|
||||
storeFile project.file('debug.keystore')
|
||||
storePassword "android"
|
||||
keyAlias "androiddebugkey"
|
||||
keyPassword "android"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
debuggable true
|
||||
jniDebuggable true
|
||||
applicationIdSuffix ".beta"
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
/*debugAsan {
|
||||
debuggable true
|
||||
jniDebuggable true
|
||||
applicationIdSuffix ".beta"
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
|
||||
packagingOptions {
|
||||
doNotStrip "**.so"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs {
|
||||
srcDir {
|
||||
'jniLibs'
|
||||
}
|
||||
}
|
||||
resources {
|
||||
srcDir {
|
||||
'jniRes'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
debugMultidex {
|
||||
initWith debug
|
||||
minifyEnabled false
|
||||
shrinkResources true
|
||||
multiDexEnabled true
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
HA {
|
||||
releaseNoGcm {
|
||||
initWith debug
|
||||
debuggable false
|
||||
jniDebuggable false
|
||||
applicationIdSuffix ".beta"
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
release {
|
||||
debuggable false
|
||||
jniDebuggable false
|
||||
minifyEnabled true
|
||||
shrinkResources false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
multiDexEnabled true
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
initWith releaseNoGcm
|
||||
}
|
||||
|
||||
foss {
|
||||
initWith release
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDirs = ['src/main/java', 'src/multidex/java']
|
||||
}
|
||||
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||
jniLibs.srcDir 'src/main/libs'
|
||||
jni.srcDirs = []
|
||||
}
|
||||
|
||||
/*sourceSets.debugAsan {
|
||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||
}*/
|
||||
|
||||
sourceSets.debugMultidex {
|
||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||
java.srcDirs = ['src/multidex/java']
|
||||
}
|
||||
|
||||
sourceSets.HA {
|
||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||
sourceSets.releaseNoGcm {
|
||||
jniLibs.srcDir 'src/main/libs'
|
||||
jni.srcDirs = []
|
||||
}
|
||||
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest.xml'
|
||||
java.srcDirs = ['src/multidex/java']
|
||||
jniLibs.srcDir 'src/main/libs'
|
||||
jni.srcDirs = []
|
||||
java.srcDirs = ['/src/gservcies/java']
|
||||
manifest.srcFile '/src/gservcies/AndroidManifest.xml'
|
||||
}
|
||||
|
||||
sourceSets.foss {
|
||||
jni.srcDirs = ['./jni/']
|
||||
}
|
||||
|
||||
flavorDimensions "version"
|
||||
|
||||
splits {
|
||||
|
||||
abi {
|
||||
|
||||
enable true
|
||||
|
||||
reset()
|
||||
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
|
||||
universalApk true
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 2
|
||||
|
||||
flavorDimensions "minApi"
|
||||
|
||||
productFlavors {
|
||||
armv7 {
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a"
|
||||
}
|
||||
ext {
|
||||
abiVersionCode = 1
|
||||
}
|
||||
}
|
||||
x86 {
|
||||
ndk {
|
||||
abiFilters "x86"
|
||||
}
|
||||
ext {
|
||||
abiVersionCode = 2
|
||||
}
|
||||
}
|
||||
armv7_SDK23 {
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a"
|
||||
}
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
minSdkVersion 23
|
||||
ext {
|
||||
abiVersionCode = 3
|
||||
}
|
||||
}
|
||||
x86_SDK23 {
|
||||
ndk {
|
||||
abiFilters "x86"
|
||||
}
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
minSdkVersion 23
|
||||
ext {
|
||||
abiVersionCode = 4
|
||||
}
|
||||
}
|
||||
arm64 {
|
||||
ndk {
|
||||
abiFilters "arm64-v8a"
|
||||
}
|
||||
ext {
|
||||
abiVersionCode = 5
|
||||
}
|
||||
}
|
||||
x64 {
|
||||
ndk {
|
||||
abiFilters "x86_64"
|
||||
}
|
||||
ext {
|
||||
abiVersionCode = 6
|
||||
}
|
||||
}
|
||||
arm64_SDK23 {
|
||||
ndk {
|
||||
abiFilters "arm64-v8a"
|
||||
}
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
/*sourceSets.debugAsan {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}*/
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
minSdkVersion 23
|
||||
ext {
|
||||
abiVersionCode = 7
|
||||
}
|
||||
}
|
||||
x64_SDK23 {
|
||||
ndk {
|
||||
abiFilters "x86_64"
|
||||
}
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest_SDK23.xml'
|
||||
}
|
||||
minSdkVersion 23
|
||||
ext {
|
||||
abiVersionCode = 8
|
||||
}
|
||||
}
|
||||
afat {
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
sourceSets.main {
|
||||
java.srcDirs = ['src/main/java', 'src/multidex/java']
|
||||
}
|
||||
/*sourceSets.debugAsan {
|
||||
manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml'
|
||||
}*/
|
||||
sourceSets.release {
|
||||
manifest.srcFile 'config/release/AndroidManifest_SDK23.xml'
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
ext {
|
||||
abiVersionCode = 9
|
||||
multiDexEnabled true
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
dependencies {
|
||||
//implementation project(path: ':ss-libev', configuration: 'default')
|
||||
implementation files('libs/ss-libev-release.aar')
|
||||
implementation files('libs/ssr-libev-release.aar')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 10 * 1875
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
output.getProcessManifestProvider().get().doLast {
|
||||
def abiVersion = variant.productFlavors.get(0).abiVersionCode
|
||||
|
||||
def outputDir = manifestOutputDirectory
|
||||
File directory
|
||||
if (outputDir instanceof File) {
|
||||
directory = outputDir
|
||||
} else {
|
||||
directory = outputDir.get().asFile
|
||||
}
|
||||
|
||||
String manifestPath = directory.toString() + "/AndroidManifest.xml"
|
||||
def manifestContent = file(manifestPath).getText()
|
||||
|
||||
manifestContent = manifestContent.replace(String.format('android:versionCode="%d"', defaultConfig.versionCode), String.format('android:versionCode="%s"', defaultConfig.versionCode + abiVersion))
|
||||
file(manifestPath).write(manifestContent)
|
||||
legacy {
|
||||
versionNameSuffix '-legacy'
|
||||
sourceSets.main {
|
||||
java.srcDirs = ['src/main/java', 'src/multidex/java']
|
||||
}
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
multiDexEnabled true
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
}
|
||||
}
|
||||
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
if (variant.buildType.name != "release" && !names.contains("afat")) {
|
||||
setIgnore(true)
|
||||
minApi21 {
|
||||
minSdkVersion 21
|
||||
versionNameSuffix '-minApi21'
|
||||
dependencies {
|
||||
//implementation project(path: ':ss-libev', configuration: 'default')
|
||||
implementation files('libs/ss-libev-release.aar')
|
||||
implementation files('libs/ssr-libev-release.aar')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
versionName "5.15.0.3"
|
||||
targetSdkVersion 29
|
||||
versionName "5.15.0.28"
|
||||
|
||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||
|
||||
def appId = null
|
||||
def appHash = null
|
||||
|
||||
//obtain your own keys at https://core.telegram.org/api/obtaining_api_id
|
||||
|
||||
if (project.rootProject.file('local.properties').exists()) {
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
appId = properties.getProperty("TELEGRAM_APP_ID") ?: System.getenv("TELEGRAM_APP_ID")
|
||||
appHash = properties.getProperty("TELEGRAM_APP_HASH") ?: System.getenv("TELEGRAM_APP_HASH")
|
||||
}
|
||||
|
||||
buildConfigField 'int', 'APP_ID', appId != null ? appId : "1391584"
|
||||
buildConfigField 'String', 'APP_HASH', "\"" + (appHash != null ? appHash : "355c91550b0d658cfb7ff89dcf91a08c") + "\""
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments "NDK_APPLICATION_MK:=jni/Application.mk", "APP_PLATFORM:=android-16", "--jobs=8"
|
||||
|
@ -303,6 +264,30 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
manifestPlaceholders = [applicationClassName: "ApplicationLoader"]
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
|
||||
}
|
||||
|
||||
tasks.all { task ->
|
||||
if (((task.name.endsWith('Ndk') || task.name.startsWith('generateJsonModel') || task.name.startsWith('externalNativeBuild'))) && !task.name.contains("Foss")) {
|
||||
task.enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
android {
|
||||
|
||||
tasks.all { task ->
|
||||
if (((task.name.contains('Crashlytics'))) && !task.name.endsWith("Release")) {
|
||||
task.enabled = false
|
||||
}
|
||||
if (((task.name.endsWith('GoogleServices'))) && !task.name.endsWith("ReleaseGoogleServices")) {
|
||||
task.enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:label="@string/NekogramBeta"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:label="@string/NekogramBeta"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:name=".ApplicationLoader"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:label="@string/Nekogram"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:label="@string/Nekogram"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
Binary file not shown.
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "124118418489",
|
||||
"firebase_url": "https://nekox-114.firebaseio.com",
|
||||
"project_id": "nekox-114",
|
||||
"storage_bucket": "nekox-114.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:124118418489:android:3dc02bf5a16e92600d39e8",
|
||||
"android_client_info": {
|
||||
"package_name": "nekox.messenger"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "124118418489-lm9tuk7lbedjal3tgr8g68id1ue8gv2m.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyAGMJyrCKui9kUWANoRYbEa3Gt4fsqm1sE"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "124118418489-lm9tuk7lbedjal3tgr8g68id1ue8gv2m.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
|
@ -580,4 +580,41 @@ LOCAL_SRC_FILES += \
|
|||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## shadowsocks-libev local
|
||||
########################################################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
SHADOWSOCKS_SOURCES := local.c \
|
||||
cache.c udprelay.c utils.c netutils.c json.c jconf.c \
|
||||
acl.c http.c tls.c rule.c \
|
||||
crypto.c aead.c stream.c base64.c \
|
||||
plugin.c ppbloom.c \
|
||||
android.c
|
||||
|
||||
LOCAL_MODULE := ss-local
|
||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_LOCAL \
|
||||
-DUSE_CRYPTO_MBEDTLS -DHAVE_CONFIG_H \
|
||||
-DCONNECT_IN_PROGRESS=EINPROGRESS \
|
||||
-I$(LOCAL_PATH)/include/shadowsocks-libev \
|
||||
-I$(LOCAL_PATH)/include \
|
||||
-I$(LOCAL_PATH)/libancillary \
|
||||
-I$(LOCAL_PATH)/mbedtls/include \
|
||||
-I$(LOCAL_PATH)/pcre \
|
||||
-I$(LOCAL_PATH)/libsodium/src/libsodium/include \
|
||||
-I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
|
||||
-I$(LOCAL_PATH)/libev
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom \
|
||||
libsodium libancillary libpcre
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
|
||||
include $(BUILD_SHARED_EXECUTABLE)
|
||||
|
||||
$(call import-module,android/cpufeatures)
|
|
@ -1,4 +1,4 @@
|
|||
APP_PLATFORM := android-16
|
||||
APP_PLATFORM := android-18
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
APP_STL := c++_static
|
||||
APP_SHORT_COMMANDS := true
|
|
@ -322,7 +322,7 @@ class Delegate : public ConnectiosManagerDelegate {
|
|||
}
|
||||
};
|
||||
|
||||
void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring ip) {
|
||||
void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring ip,jboolean ipv6) {
|
||||
const char *ipStr = env->GetStringUTFChars(ip, 0);
|
||||
const char *hostStr = env->GetStringUTFChars(host, 0);
|
||||
std::string i = std::string(ipStr);
|
||||
|
@ -334,7 +334,7 @@ void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstr
|
|||
env->ReleaseStringUTFChars(host, hostStr);
|
||||
}
|
||||
ConnectionSocket *socket = (ConnectionSocket *) (intptr_t) address;
|
||||
socket->onHostNameResolved(h, i, false);
|
||||
socket->onHostNameResolved(h, i, ipv6);
|
||||
}
|
||||
|
||||
void setLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode) {
|
||||
|
@ -445,7 +445,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
|
|||
{"native_setJava", "(Z)V", (void *) setJava},
|
||||
{"native_applyDnsConfig", "(IJLjava/lang/String;I)V", (void *) applyDnsConfig},
|
||||
{"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy},
|
||||
{"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;)V", (void *) onHostNameResolved}
|
||||
{"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;Z)V", (void *) onHostNameResolved}
|
||||
};
|
||||
|
||||
inline int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodsCount) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,61 +1,61 @@
|
|||
/*
|
||||
FAST-EDGE
|
||||
Copyright (c) 2009 Benjamin C. Haynor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _FASTEDGE
|
||||
#define _FASTEDGE
|
||||
|
||||
namespace ocr{
|
||||
//#define WIDTH 640 // uncomment to define width for situations where width is always known
|
||||
//#define HEIGHT 480 // uncomment to define heigh for situations where height is always known
|
||||
|
||||
//#define CLOCK // uncomment to show running times of image processing functions (in seconds)
|
||||
//#define ABS_APPROX // uncomment to use the absolute value approximation of sqrt(Gx ^ 2 + Gy ^2)
|
||||
/*
|
||||
FAST-EDGE
|
||||
Copyright (c) 2009 Benjamin C. Haynor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _FASTEDGE
|
||||
#define _FASTEDGE
|
||||
|
||||
namespace ocr{
|
||||
//#define WIDTH 640 // uncomment to define width for situations where width is always known
|
||||
//#define HEIGHT 480 // uncomment to define heigh for situations where height is always known
|
||||
|
||||
//#define CLOCK // uncomment to show running times of image processing functions (in seconds)
|
||||
//#define ABS_APPROX // uncomment to use the absolute value approximation of sqrt(Gx ^ 2 + Gy ^2)
|
||||
//#define PRINT_HISTOGRAM // uncomment to print the histogram used to estimate the threshold
|
||||
struct image {
|
||||
int width;
|
||||
int height;
|
||||
unsigned char * pixel_data;
|
||||
};
|
||||
|
||||
void canny_edge_detect(struct image * img_in, struct image * img_out);
|
||||
void gaussian_noise_reduce(struct image * img_in, struct image * img_out);
|
||||
void calc_gradient_sobel(struct image * img_in, int g[], int dir[]);
|
||||
void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]);
|
||||
void non_max_suppression(struct image * img, int g[], int dir[]);
|
||||
void estimate_threshold(struct image * img, int * high, int * low);
|
||||
void hysteresis (int high, int low, struct image * img_in, struct image * img_out);
|
||||
int trace (int x, int y, int low, struct image * img_in, struct image * img_out);
|
||||
int range (struct image * img, int x, int y);
|
||||
void dilate_1d_h(struct image * img, struct image * img_out);
|
||||
void dilate_1d_v(struct image * img, struct image * img_out);
|
||||
void erode_1d_h(struct image * img, struct image * img_out);
|
||||
void erode_1d_v(struct image * img, struct image * img_out);
|
||||
void erode(struct image * img_in, struct image * img_scratch, struct image * img_out);
|
||||
void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out);
|
||||
void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out);
|
||||
};
|
||||
|
||||
void canny_edge_detect(struct image * img_in, struct image * img_out);
|
||||
void gaussian_noise_reduce(struct image * img_in, struct image * img_out);
|
||||
void calc_gradient_sobel(struct image * img_in, int g[], int dir[]);
|
||||
void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]);
|
||||
void non_max_suppression(struct image * img, int g[], int dir[]);
|
||||
void estimate_threshold(struct image * img, int * high, int * low);
|
||||
void hysteresis (int high, int low, struct image * img_in, struct image * img_out);
|
||||
int trace (int x, int y, int low, struct image * img_in, struct image * img_out);
|
||||
int range (struct image * img, int x, int y);
|
||||
void dilate_1d_h(struct image * img, struct image * img_out);
|
||||
void dilate_1d_v(struct image * img, struct image * img_out);
|
||||
void erode_1d_h(struct image * img, struct image * img_out);
|
||||
void erode_1d_v(struct image * img, struct image * img_out);
|
||||
void erode(struct image * img_in, struct image * img_scratch, struct image * img_out);
|
||||
void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out);
|
||||
void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out);
|
||||
void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,469 +1,469 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <collection.h>
|
||||
#include "CXWrapper.h"
|
||||
#include <wrl.h>
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace libtgvoip;
|
||||
using namespace Platform;
|
||||
using namespace tgvoip;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Security::Cryptography::Core;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Data::Json;
|
||||
using namespace Windows::Phone::Media::Devices;
|
||||
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha1Hash;
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha256Hash;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha1Provider;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha256Provider;
|
||||
SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider;
|
||||
|
||||
/*struct tgvoip_cx_data{
|
||||
VoIPControllerWrapper^ self;
|
||||
};*/
|
||||
|
||||
VoIPControllerWrapper::VoIPControllerWrapper(){
|
||||
VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt;
|
||||
VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt;
|
||||
VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt;
|
||||
VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1;
|
||||
VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256;
|
||||
VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes;
|
||||
MicrosoftCryptoImpl::Init();
|
||||
controller=new VoIPController();
|
||||
controller->implData=(void*)this;
|
||||
VoIPController::Callbacks callbacks={0};
|
||||
callbacks.connectionStateChanged=VoIPControllerWrapper::OnStateChanged;
|
||||
callbacks.signalBarCountChanged=VoIPControllerWrapper::OnSignalBarsChanged;
|
||||
controller->SetCallbacks(callbacks);
|
||||
}
|
||||
|
||||
VoIPControllerWrapper::~VoIPControllerWrapper(){
|
||||
controller->Stop();
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Start(){
|
||||
controller->Start();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Connect(){
|
||||
controller->Connect();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array<libtgvoip::Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer){
|
||||
std::vector<tgvoip::Endpoint> eps;
|
||||
for (unsigned int i = 0; i < endpoints->Length; i++)
|
||||
{
|
||||
libtgvoip::Endpoint^ _ep = endpoints[i];
|
||||
tgvoip::Endpoint ep;
|
||||
ep.id = _ep->id;
|
||||
ep.type = tgvoip::Endpoint::Type::UDP_RELAY;
|
||||
char buf[128];
|
||||
if (_ep->ipv4){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.address = IPv4Address(buf);
|
||||
}
|
||||
if (_ep->ipv6){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.v6address = IPv6Address(buf);
|
||||
}
|
||||
ep.port = _ep->port;
|
||||
if (_ep->peerTag->Length != 16)
|
||||
throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long");
|
||||
memcpy(ep.peerTag, _ep->peerTag->Data, 16);
|
||||
eps.push_back(ep);
|
||||
}
|
||||
controller->SetRemoteEndpoints(eps, allowP2P, connectionMaxLayer);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetNetworkType(NetworkType type){
|
||||
controller->SetNetworkType((int)type);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetMicMute(bool mute){
|
||||
controller->SetMicMute(mute);
|
||||
}
|
||||
|
||||
int64 VoIPControllerWrapper::GetPreferredRelayID(){
|
||||
return controller->GetPreferredRelayID();
|
||||
}
|
||||
|
||||
int32_t VoIPControllerWrapper::GetConnectionMaxLayer(){
|
||||
return tgvoip::VoIPController::GetConnectionMaxLayer();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing){
|
||||
if(key->Length!=256)
|
||||
throw ref new Platform::InvalidArgumentException("Encryption key must be exactly 256 bytes long");
|
||||
controller->SetEncryptionKey((char*)key->Data, isOutgoing);
|
||||
}
|
||||
|
||||
int VoIPControllerWrapper::GetSignalBarsCount(){
|
||||
return controller->GetSignalBarsCount();
|
||||
}
|
||||
|
||||
CallState VoIPControllerWrapper::GetConnectionState(){
|
||||
return (CallState)controller->GetConnectionState();
|
||||
}
|
||||
|
||||
TrafficStats^ VoIPControllerWrapper::GetStats(){
|
||||
tgvoip::VoIPController::TrafficStats _stats;
|
||||
controller->GetStats(&_stats);
|
||||
|
||||
TrafficStats^ stats = ref new TrafficStats();
|
||||
stats->bytesSentWifi = _stats.bytesSentWifi;
|
||||
stats->bytesSentMobile = _stats.bytesSentMobile;
|
||||
stats->bytesRecvdWifi = _stats.bytesRecvdWifi;
|
||||
stats->bytesRecvdMobile = _stats.bytesRecvdMobile;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugString(){
|
||||
std::string log = controller->GetDebugString();
|
||||
size_t len = sizeof(wchar_t)*(log.length() + 1);
|
||||
wchar_t* wlog = (wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len / sizeof(wchar_t));
|
||||
Platform::String^ res = ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugLog(){
|
||||
std::string log=controller->GetDebugLog();
|
||||
size_t len=sizeof(wchar_t)*(log.length()+1);
|
||||
wchar_t* wlog=(wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len/sizeof(wchar_t));
|
||||
Platform::String^ res=ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Error VoIPControllerWrapper::GetLastError(){
|
||||
return (Error)controller->GetLastError();
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetVersion(){
|
||||
const char* v=VoIPController::GetVersion();
|
||||
wchar_t buf[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, v, -1, buf, sizeof(buf));
|
||||
return ref new Platform::String(buf);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChanged(VoIPController* c, int state){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnStateChangedInternal(state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChanged(VoIPController* c, int count){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnSignalBarsChangedInternal(count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChangedInternal(int state){
|
||||
CallStateChanged(this, (CallState)state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChangedInternal(int count){
|
||||
SignalBarsChanged(this, count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetConfig(VoIPConfig^ wrapper){
|
||||
VoIPController::Config config{0};
|
||||
config.initTimeout=wrapper->initTimeout;
|
||||
config.recvTimeout=wrapper->recvTimeout;
|
||||
config.dataSaving=(int)wrapper->dataSaving;
|
||||
config.logFilePath;
|
||||
config.statsDumpFilePath;
|
||||
|
||||
config.enableAEC=wrapper->enableAEC;
|
||||
config.enableNS=wrapper->enableNS;
|
||||
config.enableAGC=wrapper->enableAGC;
|
||||
|
||||
config.enableCallUpgrade=wrapper->enableCallUpgrade;
|
||||
|
||||
config.logPacketStats=wrapper->logPacketStats;
|
||||
config.enableVolumeControl=wrapper->enableVolumeControl;
|
||||
|
||||
config.enableVideoSend=wrapper->enableVideoSend;
|
||||
config.enableVideoReceive=wrapper->enableVideoReceive;
|
||||
|
||||
if(wrapper->logFilePath!=nullptr&&!wrapper->logFilePath->IsEmpty()){
|
||||
config.logFilePath = wstring(wrapper->logFilePath->Data());
|
||||
}
|
||||
if (wrapper->statsDumpFilePath != nullptr&&!wrapper->statsDumpFilePath->IsEmpty()){
|
||||
config.statsDumpFilePath = wstring(wrapper->statsDumpFilePath->Data());
|
||||
}
|
||||
|
||||
controller->SetConfig(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password){
|
||||
char _address[2000];
|
||||
char _username[256];
|
||||
char _password[256];
|
||||
|
||||
WideCharToMultiByte(CP_UTF8, 0, address->Data(), -1, _address, sizeof(_address), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, username->Data(), -1, _username, sizeof(_username), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, password->Data(), -1, _password, sizeof(_password), NULL, NULL);
|
||||
|
||||
controller->SetProxy((int)protocol, _address, port, _username, _password);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetAudioOutputGainControlEnabled(bool enabled){
|
||||
controller->SetAudioOutputGainControlEnabled(enabled);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetInputVolume(float level){
|
||||
controller->SetInputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetOutputVolume(float level){
|
||||
controller->SetOutputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){
|
||||
std::string config=ToUtf8(json->Data(), json->Length());
|
||||
ServerConfig::GetSharedInstance()->Update(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SwitchSpeaker(bool external){
|
||||
auto routingManager = AudioRoutingManager::GetDefault();
|
||||
if (external){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone);
|
||||
}
|
||||
else{
|
||||
if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Bluetooth) == AvailableAudioRoutingEndpoints::Bluetooth){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Bluetooth);
|
||||
}
|
||||
else if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Earpiece) == AvailableAudioRoutingEndpoints::Earpiece){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Earpiece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv+16;
|
||||
uint8_t* yPrev=iv;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv;
|
||||
uint8_t* yPrev=iv+16;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Decrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
#define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3]))
|
||||
#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
|
||||
|
||||
typedef uint8_t u8;
|
||||
|
||||
#define L_ENDIAN
|
||||
|
||||
/* increment counter (128-bit int) by 2^64 */
|
||||
static void AES_ctr128_inc(unsigned char *counter) {
|
||||
unsigned long c;
|
||||
|
||||
/* Grab 3rd dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 8);
|
||||
c++;
|
||||
PUTU32(counter + 8, c);
|
||||
#else
|
||||
c = GETU32(counter + 4);
|
||||
c++;
|
||||
PUTU32(counter + 4, c);
|
||||
#endif
|
||||
|
||||
/* if no overflow, we're done */
|
||||
if (c)
|
||||
return;
|
||||
|
||||
/* Grab top dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 12);
|
||||
c++;
|
||||
PUTU32(counter + 12, c);
|
||||
#else
|
||||
c = GETU32(counter + 0);
|
||||
c++;
|
||||
PUTU32(counter + 0, c);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){
|
||||
unsigned int n;
|
||||
unsigned long l = len;
|
||||
|
||||
//assert(in && out && key && counter && num);
|
||||
//assert(*num < AES_BLOCK_SIZE);
|
||||
|
||||
IBuffer^ keybuf = IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
|
||||
n = *num;
|
||||
|
||||
while (l--) {
|
||||
if (n == 0) {
|
||||
IBuffer^ inbuf = IBufferFromPtr(counter, 16);
|
||||
IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, ecount_buf);
|
||||
//AES_encrypt(counter, ecount_buf, key);
|
||||
AES_ctr128_inc(counter);
|
||||
}
|
||||
*inout = *(inout++) ^ ecount_buf[n];
|
||||
n = (n + 1) % 16;
|
||||
}
|
||||
|
||||
*num = n;
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha1Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 20, out);
|
||||
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA256(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha256Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 32, out);
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::RandBytes(uint8_t* buffer, size_t len){
|
||||
IBuffer^ res=CryptographicBuffer::GenerateRandom(len);
|
||||
IBufferToPtr(res, len, buffer);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::Init(){
|
||||
/*sha1Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->CreateHash();
|
||||
sha256Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256)->CreateHash();*/
|
||||
sha1Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1);
|
||||
sha256Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256);
|
||||
aesKeyProvider=SymmetricKeyAlgorithmProvider::OpenAlgorithm(SymmetricAlgorithmNames::AesEcb);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::XorInt128(uint8_t* a, uint8_t* b, uint8_t* out){
|
||||
uint64_t* _a=reinterpret_cast<uint64_t*>(a);
|
||||
uint64_t* _b=reinterpret_cast<uint64_t*>(b);
|
||||
uint64_t* _out=reinterpret_cast<uint64_t*>(out);
|
||||
_out[0]=_a[0]^_b[0];
|
||||
_out[1]=_a[1]^_b[1];
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::IBufferToPtr(IBuffer^ buffer, size_t len, uint8_t* out)
|
||||
{
|
||||
ComPtr<IBufferByteAccess> bufferByteAccess;
|
||||
reinterpret_cast<IInspectable*>(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
|
||||
|
||||
byte* hashBuffer;
|
||||
bufferByteAccess->Buffer(&hashBuffer);
|
||||
CopyMemory(out, hashBuffer, len);
|
||||
}
|
||||
|
||||
IBuffer^ MicrosoftCryptoImpl::IBufferFromPtr(uint8_t* msg, size_t len)
|
||||
{
|
||||
ComPtr<NativeBuffer> nativeBuffer=Make<NativeBuffer>((byte *)msg, len);
|
||||
return reinterpret_cast<IBuffer^>(nativeBuffer.Get());
|
||||
}
|
||||
|
||||
/*Platform::String^ VoIPControllerWrapper::TestAesIge(){
|
||||
MicrosoftCryptoImpl::Init();
|
||||
Platform::String^ res="";
|
||||
Platform::Array<uint8>^ data=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ out=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ key=ref new Platform::Array<uint8>(16);
|
||||
Platform::Array<uint8>^ iv=ref new Platform::Array<uint8>(32);
|
||||
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("0000000000000000000000000000000000000000000000000000000000000000"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("99706487A1CDE613BC6DE0B6F24B1C7AA448C8B9C3403E3467A8CAD89340F53B"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("4C2E204C6574277320686F70652042656E20676F74206974207269676874210A"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
return res;
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <collection.h>
|
||||
#include "CXWrapper.h"
|
||||
#include <wrl.h>
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace libtgvoip;
|
||||
using namespace Platform;
|
||||
using namespace tgvoip;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Security::Cryptography::Core;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Data::Json;
|
||||
using namespace Windows::Phone::Media::Devices;
|
||||
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha1Hash;
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha256Hash;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha1Provider;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha256Provider;
|
||||
SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider;
|
||||
|
||||
/*struct tgvoip_cx_data{
|
||||
VoIPControllerWrapper^ self;
|
||||
};*/
|
||||
|
||||
VoIPControllerWrapper::VoIPControllerWrapper(){
|
||||
VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt;
|
||||
VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt;
|
||||
VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt;
|
||||
VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1;
|
||||
VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256;
|
||||
VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes;
|
||||
MicrosoftCryptoImpl::Init();
|
||||
controller=new VoIPController();
|
||||
controller->implData=(void*)this;
|
||||
VoIPController::Callbacks callbacks={0};
|
||||
callbacks.connectionStateChanged=VoIPControllerWrapper::OnStateChanged;
|
||||
callbacks.signalBarCountChanged=VoIPControllerWrapper::OnSignalBarsChanged;
|
||||
controller->SetCallbacks(callbacks);
|
||||
}
|
||||
|
||||
VoIPControllerWrapper::~VoIPControllerWrapper(){
|
||||
controller->Stop();
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Start(){
|
||||
controller->Start();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Connect(){
|
||||
controller->Connect();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array<libtgvoip::Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer){
|
||||
std::vector<tgvoip::Endpoint> eps;
|
||||
for (unsigned int i = 0; i < endpoints->Length; i++)
|
||||
{
|
||||
libtgvoip::Endpoint^ _ep = endpoints[i];
|
||||
tgvoip::Endpoint ep;
|
||||
ep.id = _ep->id;
|
||||
ep.type = tgvoip::Endpoint::Type::UDP_RELAY;
|
||||
char buf[128];
|
||||
if (_ep->ipv4){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.address = IPv4Address(buf);
|
||||
}
|
||||
if (_ep->ipv6){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.v6address = IPv6Address(buf);
|
||||
}
|
||||
ep.port = _ep->port;
|
||||
if (_ep->peerTag->Length != 16)
|
||||
throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long");
|
||||
memcpy(ep.peerTag, _ep->peerTag->Data, 16);
|
||||
eps.push_back(ep);
|
||||
}
|
||||
controller->SetRemoteEndpoints(eps, allowP2P, connectionMaxLayer);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetNetworkType(NetworkType type){
|
||||
controller->SetNetworkType((int)type);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetMicMute(bool mute){
|
||||
controller->SetMicMute(mute);
|
||||
}
|
||||
|
||||
int64 VoIPControllerWrapper::GetPreferredRelayID(){
|
||||
return controller->GetPreferredRelayID();
|
||||
}
|
||||
|
||||
int32_t VoIPControllerWrapper::GetConnectionMaxLayer(){
|
||||
return tgvoip::VoIPController::GetConnectionMaxLayer();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing){
|
||||
if(key->Length!=256)
|
||||
throw ref new Platform::InvalidArgumentException("Encryption key must be exactly 256 bytes long");
|
||||
controller->SetEncryptionKey((char*)key->Data, isOutgoing);
|
||||
}
|
||||
|
||||
int VoIPControllerWrapper::GetSignalBarsCount(){
|
||||
return controller->GetSignalBarsCount();
|
||||
}
|
||||
|
||||
CallState VoIPControllerWrapper::GetConnectionState(){
|
||||
return (CallState)controller->GetConnectionState();
|
||||
}
|
||||
|
||||
TrafficStats^ VoIPControllerWrapper::GetStats(){
|
||||
tgvoip::VoIPController::TrafficStats _stats;
|
||||
controller->GetStats(&_stats);
|
||||
|
||||
TrafficStats^ stats = ref new TrafficStats();
|
||||
stats->bytesSentWifi = _stats.bytesSentWifi;
|
||||
stats->bytesSentMobile = _stats.bytesSentMobile;
|
||||
stats->bytesRecvdWifi = _stats.bytesRecvdWifi;
|
||||
stats->bytesRecvdMobile = _stats.bytesRecvdMobile;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugString(){
|
||||
std::string log = controller->GetDebugString();
|
||||
size_t len = sizeof(wchar_t)*(log.length() + 1);
|
||||
wchar_t* wlog = (wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len / sizeof(wchar_t));
|
||||
Platform::String^ res = ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugLog(){
|
||||
std::string log=controller->GetDebugLog();
|
||||
size_t len=sizeof(wchar_t)*(log.length()+1);
|
||||
wchar_t* wlog=(wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len/sizeof(wchar_t));
|
||||
Platform::String^ res=ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Error VoIPControllerWrapper::GetLastError(){
|
||||
return (Error)controller->GetLastError();
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetVersion(){
|
||||
const char* v=VoIPController::GetVersion();
|
||||
wchar_t buf[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, v, -1, buf, sizeof(buf));
|
||||
return ref new Platform::String(buf);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChanged(VoIPController* c, int state){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnStateChangedInternal(state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChanged(VoIPController* c, int count){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnSignalBarsChangedInternal(count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChangedInternal(int state){
|
||||
CallStateChanged(this, (CallState)state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChangedInternal(int count){
|
||||
SignalBarsChanged(this, count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetConfig(VoIPConfig^ wrapper){
|
||||
VoIPController::Config config{0};
|
||||
config.initTimeout=wrapper->initTimeout;
|
||||
config.recvTimeout=wrapper->recvTimeout;
|
||||
config.dataSaving=(int)wrapper->dataSaving;
|
||||
config.logFilePath;
|
||||
config.statsDumpFilePath;
|
||||
|
||||
config.enableAEC=wrapper->enableAEC;
|
||||
config.enableNS=wrapper->enableNS;
|
||||
config.enableAGC=wrapper->enableAGC;
|
||||
|
||||
config.enableCallUpgrade=wrapper->enableCallUpgrade;
|
||||
|
||||
config.logPacketStats=wrapper->logPacketStats;
|
||||
config.enableVolumeControl=wrapper->enableVolumeControl;
|
||||
|
||||
config.enableVideoSend=wrapper->enableVideoSend;
|
||||
config.enableVideoReceive=wrapper->enableVideoReceive;
|
||||
|
||||
if(wrapper->logFilePath!=nullptr&&!wrapper->logFilePath->IsEmpty()){
|
||||
config.logFilePath = wstring(wrapper->logFilePath->Data());
|
||||
}
|
||||
if (wrapper->statsDumpFilePath != nullptr&&!wrapper->statsDumpFilePath->IsEmpty()){
|
||||
config.statsDumpFilePath = wstring(wrapper->statsDumpFilePath->Data());
|
||||
}
|
||||
|
||||
controller->SetConfig(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password){
|
||||
char _address[2000];
|
||||
char _username[256];
|
||||
char _password[256];
|
||||
|
||||
WideCharToMultiByte(CP_UTF8, 0, address->Data(), -1, _address, sizeof(_address), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, username->Data(), -1, _username, sizeof(_username), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, password->Data(), -1, _password, sizeof(_password), NULL, NULL);
|
||||
|
||||
controller->SetProxy((int)protocol, _address, port, _username, _password);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetAudioOutputGainControlEnabled(bool enabled){
|
||||
controller->SetAudioOutputGainControlEnabled(enabled);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetInputVolume(float level){
|
||||
controller->SetInputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetOutputVolume(float level){
|
||||
controller->SetOutputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){
|
||||
std::string config=ToUtf8(json->Data(), json->Length());
|
||||
ServerConfig::GetSharedInstance()->Update(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SwitchSpeaker(bool external){
|
||||
auto routingManager = AudioRoutingManager::GetDefault();
|
||||
if (external){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone);
|
||||
}
|
||||
else{
|
||||
if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Bluetooth) == AvailableAudioRoutingEndpoints::Bluetooth){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Bluetooth);
|
||||
}
|
||||
else if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Earpiece) == AvailableAudioRoutingEndpoints::Earpiece){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Earpiece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv+16;
|
||||
uint8_t* yPrev=iv;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv;
|
||||
uint8_t* yPrev=iv+16;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Decrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
#define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3]))
|
||||
#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
|
||||
|
||||
typedef uint8_t u8;
|
||||
|
||||
#define L_ENDIAN
|
||||
|
||||
/* increment counter (128-bit int) by 2^64 */
|
||||
static void AES_ctr128_inc(unsigned char *counter) {
|
||||
unsigned long c;
|
||||
|
||||
/* Grab 3rd dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 8);
|
||||
c++;
|
||||
PUTU32(counter + 8, c);
|
||||
#else
|
||||
c = GETU32(counter + 4);
|
||||
c++;
|
||||
PUTU32(counter + 4, c);
|
||||
#endif
|
||||
|
||||
/* if no overflow, we're done */
|
||||
if (c)
|
||||
return;
|
||||
|
||||
/* Grab top dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 12);
|
||||
c++;
|
||||
PUTU32(counter + 12, c);
|
||||
#else
|
||||
c = GETU32(counter + 0);
|
||||
c++;
|
||||
PUTU32(counter + 0, c);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){
|
||||
unsigned int n;
|
||||
unsigned long l = len;
|
||||
|
||||
//assert(in && out && key && counter && num);
|
||||
//assert(*num < AES_BLOCK_SIZE);
|
||||
|
||||
IBuffer^ keybuf = IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
|
||||
n = *num;
|
||||
|
||||
while (l--) {
|
||||
if (n == 0) {
|
||||
IBuffer^ inbuf = IBufferFromPtr(counter, 16);
|
||||
IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, ecount_buf);
|
||||
//AES_encrypt(counter, ecount_buf, key);
|
||||
AES_ctr128_inc(counter);
|
||||
}
|
||||
*inout = *(inout++) ^ ecount_buf[n];
|
||||
n = (n + 1) % 16;
|
||||
}
|
||||
|
||||
*num = n;
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha1Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 20, out);
|
||||
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA256(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha256Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 32, out);
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::RandBytes(uint8_t* buffer, size_t len){
|
||||
IBuffer^ res=CryptographicBuffer::GenerateRandom(len);
|
||||
IBufferToPtr(res, len, buffer);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::Init(){
|
||||
/*sha1Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->CreateHash();
|
||||
sha256Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256)->CreateHash();*/
|
||||
sha1Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1);
|
||||
sha256Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256);
|
||||
aesKeyProvider=SymmetricKeyAlgorithmProvider::OpenAlgorithm(SymmetricAlgorithmNames::AesEcb);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::XorInt128(uint8_t* a, uint8_t* b, uint8_t* out){
|
||||
uint64_t* _a=reinterpret_cast<uint64_t*>(a);
|
||||
uint64_t* _b=reinterpret_cast<uint64_t*>(b);
|
||||
uint64_t* _out=reinterpret_cast<uint64_t*>(out);
|
||||
_out[0]=_a[0]^_b[0];
|
||||
_out[1]=_a[1]^_b[1];
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::IBufferToPtr(IBuffer^ buffer, size_t len, uint8_t* out)
|
||||
{
|
||||
ComPtr<IBufferByteAccess> bufferByteAccess;
|
||||
reinterpret_cast<IInspectable*>(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
|
||||
|
||||
byte* hashBuffer;
|
||||
bufferByteAccess->Buffer(&hashBuffer);
|
||||
CopyMemory(out, hashBuffer, len);
|
||||
}
|
||||
|
||||
IBuffer^ MicrosoftCryptoImpl::IBufferFromPtr(uint8_t* msg, size_t len)
|
||||
{
|
||||
ComPtr<NativeBuffer> nativeBuffer=Make<NativeBuffer>((byte *)msg, len);
|
||||
return reinterpret_cast<IBuffer^>(nativeBuffer.Get());
|
||||
}
|
||||
|
||||
/*Platform::String^ VoIPControllerWrapper::TestAesIge(){
|
||||
MicrosoftCryptoImpl::Init();
|
||||
Platform::String^ res="";
|
||||
Platform::Array<uint8>^ data=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ out=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ key=ref new Platform::Array<uint8>(16);
|
||||
Platform::Array<uint8>^ iv=ref new Platform::Array<uint8>(32);
|
||||
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("0000000000000000000000000000000000000000000000000000000000000000"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("99706487A1CDE613BC6DE0B6F24B1C7AA448C8B9C3403E3467A8CAD89340F53B"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("4C2E204C6574277320686F70652042656E20676F74206974207269676874210A"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
return res;
|
||||
}*/
|
|
@ -1,273 +1,273 @@
|
|||
#pragma once
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <robuffer.h>
|
||||
#include <vector>
|
||||
#include "../../VoIPController.h"
|
||||
#include "../../VoIPServerConfig.h"
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
#define STACK_ARRAY(TYPE, LEN) \
|
||||
static_cast<TYPE*>(::alloca((LEN) * sizeof(TYPE)))
|
||||
|
||||
inline std::wstring ToUtf16(const char* utf8, size_t len) {
|
||||
int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len),
|
||||
nullptr, 0);
|
||||
wchar_t* ws = STACK_ARRAY(wchar_t, len16);
|
||||
::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len), ws, len16);
|
||||
return std::wstring(ws, len16);
|
||||
}
|
||||
|
||||
inline std::wstring ToUtf16(const std::string& str) {
|
||||
return ToUtf16(str.data(), str.length());
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide, size_t len) {
|
||||
int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
char* ns = STACK_ARRAY(char, len8);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len), ns, len8,
|
||||
nullptr, nullptr);
|
||||
return std::string(ns, len8);
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide) {
|
||||
return ToUtf8(wide, wcslen(wide));
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const std::wstring& wstr) {
|
||||
return ToUtf8(wstr.data(), wstr.length());
|
||||
}
|
||||
|
||||
namespace libtgvoip{
|
||||
public ref class Endpoint sealed{
|
||||
public:
|
||||
property int64 id;
|
||||
property uint16 port;
|
||||
property Platform::String^ ipv4;
|
||||
property Platform::String^ ipv6;
|
||||
property Platform::Array<uint8>^ peerTag;
|
||||
};
|
||||
|
||||
public ref class TrafficStats sealed{
|
||||
public:
|
||||
property uint64_t bytesSentWifi;
|
||||
property uint64_t bytesRecvdWifi;
|
||||
property uint64_t bytesSentMobile;
|
||||
property uint64_t bytesRecvdMobile;
|
||||
};
|
||||
|
||||
public enum class CallState : int{
|
||||
WaitInit=1,
|
||||
WaitInitAck,
|
||||
Established,
|
||||
Failed
|
||||
};
|
||||
|
||||
public enum class Error : int{
|
||||
Unknown=0,
|
||||
Incompatible,
|
||||
Timeout,
|
||||
AudioIO
|
||||
};
|
||||
|
||||
public enum class NetworkType : int{
|
||||
Unknown=0,
|
||||
GPRS,
|
||||
EDGE,
|
||||
UMTS,
|
||||
HSPA,
|
||||
LTE,
|
||||
WiFi,
|
||||
Ethernet,
|
||||
OtherHighSpeed,
|
||||
OtherLowSpeed,
|
||||
Dialup,
|
||||
OtherMobile
|
||||
};
|
||||
|
||||
public enum class DataSavingMode{
|
||||
Never=0,
|
||||
MobileOnly,
|
||||
Always
|
||||
};
|
||||
|
||||
public enum class ProxyProtocol{
|
||||
None=0,
|
||||
SOCKS5
|
||||
};
|
||||
|
||||
public ref class VoIPConfig sealed {
|
||||
public:
|
||||
VoIPConfig() {
|
||||
logPacketStats = false;
|
||||
enableVolumeControl = false;
|
||||
enableVideoSend = false;
|
||||
enableVideoReceive = false;
|
||||
}
|
||||
|
||||
property double initTimeout;
|
||||
property double recvTimeout;
|
||||
property DataSavingMode dataSaving;
|
||||
property String^ logFilePath;
|
||||
property String^ statsDumpFilePath;
|
||||
|
||||
property bool enableAEC;
|
||||
property bool enableNS;
|
||||
property bool enableAGC;
|
||||
|
||||
property bool enableCallUpgrade;
|
||||
|
||||
property bool logPacketStats;
|
||||
property bool enableVolumeControl;
|
||||
|
||||
property bool enableVideoSend;
|
||||
property bool enableVideoReceive;
|
||||
};
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void CallStateChangedEventHandler(VoIPControllerWrapper^ sender, CallState newState);
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void SignalBarsChangedEventHandler(VoIPControllerWrapper^ sender, int newCount);
|
||||
|
||||
public ref class VoIPControllerWrapper sealed{
|
||||
public:
|
||||
VoIPControllerWrapper();
|
||||
virtual ~VoIPControllerWrapper();
|
||||
void Start();
|
||||
void Connect();
|
||||
void SetPublicEndpoints(const Platform::Array<Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer);
|
||||
void SetNetworkType(NetworkType type);
|
||||
void SetMicMute(bool mute);
|
||||
void SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing);
|
||||
void SetConfig(VoIPConfig^ config);
|
||||
void SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password);
|
||||
int GetSignalBarsCount();
|
||||
CallState GetConnectionState();
|
||||
TrafficStats^ GetStats();
|
||||
Platform::String^ GetDebugString();
|
||||
Platform::String^ GetDebugLog();
|
||||
Error GetLastError();
|
||||
static Platform::String^ GetVersion();
|
||||
int64 GetPreferredRelayID();
|
||||
void SetAudioOutputGainControlEnabled(bool enabled);
|
||||
|
||||
void SetInputVolume(float level);
|
||||
void SetOutputVolume(float level);
|
||||
|
||||
property String^ CurrentAudioInput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioInputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioInput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
property String^ CurrentAudioOutput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioOutputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioOutput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetConnectionMaxLayer();
|
||||
static void UpdateServerConfig(Platform::String^ json);
|
||||
static void SwitchSpeaker(bool external);
|
||||
//static Platform::String^ TestAesIge();
|
||||
|
||||
event CallStateChangedEventHandler^ CallStateChanged;
|
||||
event SignalBarsChangedEventHandler^ SignalBarsChanged;
|
||||
|
||||
private:
|
||||
static void OnStateChanged(tgvoip::VoIPController* c, int state);
|
||||
static void OnSignalBarsChanged(tgvoip::VoIPController* c, int count);
|
||||
void OnStateChangedInternal(int state);
|
||||
void OnSignalBarsChangedInternal(int count);
|
||||
tgvoip::VoIPController* controller;
|
||||
};
|
||||
|
||||
ref class MicrosoftCryptoImpl{
|
||||
public:
|
||||
static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
|
||||
static void SHA1(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void SHA256(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void RandBytes(uint8_t* buffer, size_t len);
|
||||
static void Init();
|
||||
private:
|
||||
static inline void XorInt128(uint8_t* a, uint8_t* b, uint8_t* out);
|
||||
static void IBufferToPtr(Windows::Storage::Streams::IBuffer^ buffer, size_t len, uint8_t* out);
|
||||
static Windows::Storage::Streams::IBuffer^ IBufferFromPtr(uint8_t* msg, size_t len);
|
||||
/*static Windows::Security::Cryptography::Core::CryptographicHash^ sha1Hash;
|
||||
static Windows::Security::Cryptography::Core::CryptographicHash^ sha256Hash;*/
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha1Provider;
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha256Provider;
|
||||
static Windows::Security::Cryptography::Core::SymmetricKeyAlgorithmProvider^ aesKeyProvider;
|
||||
};
|
||||
|
||||
class NativeBuffer :
|
||||
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
|
||||
ABI::Windows::Storage::Streams::IBuffer,
|
||||
Windows::Storage::Streams::IBufferByteAccess>
|
||||
{
|
||||
public:
|
||||
NativeBuffer(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
}
|
||||
|
||||
virtual ~NativeBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP Buffer(byte **value)
|
||||
{
|
||||
*value=m_buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Capacity(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Length(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP put_Length(UINT32 value)
|
||||
{
|
||||
m_length=value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
UINT32 m_length;
|
||||
byte *m_buffer;
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <robuffer.h>
|
||||
#include <vector>
|
||||
#include "../../VoIPController.h"
|
||||
#include "../../VoIPServerConfig.h"
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
#define STACK_ARRAY(TYPE, LEN) \
|
||||
static_cast<TYPE*>(::alloca((LEN) * sizeof(TYPE)))
|
||||
|
||||
inline std::wstring ToUtf16(const char* utf8, size_t len) {
|
||||
int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len),
|
||||
nullptr, 0);
|
||||
wchar_t* ws = STACK_ARRAY(wchar_t, len16);
|
||||
::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len), ws, len16);
|
||||
return std::wstring(ws, len16);
|
||||
}
|
||||
|
||||
inline std::wstring ToUtf16(const std::string& str) {
|
||||
return ToUtf16(str.data(), str.length());
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide, size_t len) {
|
||||
int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
char* ns = STACK_ARRAY(char, len8);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len), ns, len8,
|
||||
nullptr, nullptr);
|
||||
return std::string(ns, len8);
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide) {
|
||||
return ToUtf8(wide, wcslen(wide));
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const std::wstring& wstr) {
|
||||
return ToUtf8(wstr.data(), wstr.length());
|
||||
}
|
||||
|
||||
namespace libtgvoip{
|
||||
public ref class Endpoint sealed{
|
||||
public:
|
||||
property int64 id;
|
||||
property uint16 port;
|
||||
property Platform::String^ ipv4;
|
||||
property Platform::String^ ipv6;
|
||||
property Platform::Array<uint8>^ peerTag;
|
||||
};
|
||||
|
||||
public ref class TrafficStats sealed{
|
||||
public:
|
||||
property uint64_t bytesSentWifi;
|
||||
property uint64_t bytesRecvdWifi;
|
||||
property uint64_t bytesSentMobile;
|
||||
property uint64_t bytesRecvdMobile;
|
||||
};
|
||||
|
||||
public enum class CallState : int{
|
||||
WaitInit=1,
|
||||
WaitInitAck,
|
||||
Established,
|
||||
Failed
|
||||
};
|
||||
|
||||
public enum class Error : int{
|
||||
Unknown=0,
|
||||
Incompatible,
|
||||
Timeout,
|
||||
AudioIO
|
||||
};
|
||||
|
||||
public enum class NetworkType : int{
|
||||
Unknown=0,
|
||||
GPRS,
|
||||
EDGE,
|
||||
UMTS,
|
||||
HSPA,
|
||||
LTE,
|
||||
WiFi,
|
||||
Ethernet,
|
||||
OtherHighSpeed,
|
||||
OtherLowSpeed,
|
||||
Dialup,
|
||||
OtherMobile
|
||||
};
|
||||
|
||||
public enum class DataSavingMode{
|
||||
Never=0,
|
||||
MobileOnly,
|
||||
Always
|
||||
};
|
||||
|
||||
public enum class ProxyProtocol{
|
||||
None=0,
|
||||
SOCKS5
|
||||
};
|
||||
|
||||
public ref class VoIPConfig sealed {
|
||||
public:
|
||||
VoIPConfig() {
|
||||
logPacketStats = false;
|
||||
enableVolumeControl = false;
|
||||
enableVideoSend = false;
|
||||
enableVideoReceive = false;
|
||||
}
|
||||
|
||||
property double initTimeout;
|
||||
property double recvTimeout;
|
||||
property DataSavingMode dataSaving;
|
||||
property String^ logFilePath;
|
||||
property String^ statsDumpFilePath;
|
||||
|
||||
property bool enableAEC;
|
||||
property bool enableNS;
|
||||
property bool enableAGC;
|
||||
|
||||
property bool enableCallUpgrade;
|
||||
|
||||
property bool logPacketStats;
|
||||
property bool enableVolumeControl;
|
||||
|
||||
property bool enableVideoSend;
|
||||
property bool enableVideoReceive;
|
||||
};
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void CallStateChangedEventHandler(VoIPControllerWrapper^ sender, CallState newState);
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void SignalBarsChangedEventHandler(VoIPControllerWrapper^ sender, int newCount);
|
||||
|
||||
public ref class VoIPControllerWrapper sealed{
|
||||
public:
|
||||
VoIPControllerWrapper();
|
||||
virtual ~VoIPControllerWrapper();
|
||||
void Start();
|
||||
void Connect();
|
||||
void SetPublicEndpoints(const Platform::Array<Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer);
|
||||
void SetNetworkType(NetworkType type);
|
||||
void SetMicMute(bool mute);
|
||||
void SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing);
|
||||
void SetConfig(VoIPConfig^ config);
|
||||
void SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password);
|
||||
int GetSignalBarsCount();
|
||||
CallState GetConnectionState();
|
||||
TrafficStats^ GetStats();
|
||||
Platform::String^ GetDebugString();
|
||||
Platform::String^ GetDebugLog();
|
||||
Error GetLastError();
|
||||
static Platform::String^ GetVersion();
|
||||
int64 GetPreferredRelayID();
|
||||
void SetAudioOutputGainControlEnabled(bool enabled);
|
||||
|
||||
void SetInputVolume(float level);
|
||||
void SetOutputVolume(float level);
|
||||
|
||||
property String^ CurrentAudioInput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioInputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioInput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
property String^ CurrentAudioOutput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioOutputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioOutput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetConnectionMaxLayer();
|
||||
static void UpdateServerConfig(Platform::String^ json);
|
||||
static void SwitchSpeaker(bool external);
|
||||
//static Platform::String^ TestAesIge();
|
||||
|
||||
event CallStateChangedEventHandler^ CallStateChanged;
|
||||
event SignalBarsChangedEventHandler^ SignalBarsChanged;
|
||||
|
||||
private:
|
||||
static void OnStateChanged(tgvoip::VoIPController* c, int state);
|
||||
static void OnSignalBarsChanged(tgvoip::VoIPController* c, int count);
|
||||
void OnStateChangedInternal(int state);
|
||||
void OnSignalBarsChangedInternal(int count);
|
||||
tgvoip::VoIPController* controller;
|
||||
};
|
||||
|
||||
ref class MicrosoftCryptoImpl{
|
||||
public:
|
||||
static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
|
||||
static void SHA1(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void SHA256(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void RandBytes(uint8_t* buffer, size_t len);
|
||||
static void Init();
|
||||
private:
|
||||
static inline void XorInt128(uint8_t* a, uint8_t* b, uint8_t* out);
|
||||
static void IBufferToPtr(Windows::Storage::Streams::IBuffer^ buffer, size_t len, uint8_t* out);
|
||||
static Windows::Storage::Streams::IBuffer^ IBufferFromPtr(uint8_t* msg, size_t len);
|
||||
/*static Windows::Security::Cryptography::Core::CryptographicHash^ sha1Hash;
|
||||
static Windows::Security::Cryptography::Core::CryptographicHash^ sha256Hash;*/
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha1Provider;
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha256Provider;
|
||||
static Windows::Security::Cryptography::Core::SymmetricKeyAlgorithmProvider^ aesKeyProvider;
|
||||
};
|
||||
|
||||
class NativeBuffer :
|
||||
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
|
||||
ABI::Windows::Storage::Streams::IBuffer,
|
||||
Windows::Storage::Streams::IBufferByteAccess>
|
||||
{
|
||||
public:
|
||||
NativeBuffer(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
}
|
||||
|
||||
virtual ~NativeBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP Buffer(byte **value)
|
||||
{
|
||||
*value=m_buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Capacity(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Length(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP put_Length(UINT32 value)
|
||||
{
|
||||
m_length=value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
UINT32 m_length;
|
||||
byte *m_buffer;
|
||||
};
|
||||
}
|
|
@ -1,68 +1,68 @@
|
|||
|
||||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include "WindowsSandboxUtils.h"
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifdef TGVOIP_WP_SILVERLIGHT
|
||||
#include <phoneaudioclient.h>
|
||||
#endif
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) {
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
// Did I say that I hate pointlessly asynchronous things?
|
||||
HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
ActivationHandler activationHandler(event);
|
||||
IActivateAudioInterfaceAsyncOperation* actHandler;
|
||||
HRESULT cr = ActivateAudioInterfaceAsync(devID, __uuidof(IAudioClient2), NULL, (IActivateAudioInterfaceCompletionHandler*)&activationHandler, &actHandler);
|
||||
if (callRes)
|
||||
*callRes = cr;
|
||||
DWORD resulttt = WaitForSingleObjectEx(event, INFINITE, false);
|
||||
DWORD last = GetLastError();
|
||||
CloseHandle(event);
|
||||
if (actRes)
|
||||
*actRes = activationHandler.actResult;
|
||||
return activationHandler.client;
|
||||
#else
|
||||
IAudioClient2* client;
|
||||
HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client);
|
||||
if(callRes)
|
||||
*callRes=S_OK;
|
||||
if(actRes)
|
||||
*actRes=res;
|
||||
return client;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
ActivationHandler::ActivationHandler(HANDLE _event) : event(_event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation * operation)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
HRESULT hrActivateResult = S_OK;
|
||||
IUnknown *punkAudioInterface = nullptr;
|
||||
|
||||
hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface);
|
||||
if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult))
|
||||
{
|
||||
punkAudioInterface->QueryInterface(IID_PPV_ARGS(&client));
|
||||
}
|
||||
|
||||
SetEvent(event);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include "WindowsSandboxUtils.h"
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifdef TGVOIP_WP_SILVERLIGHT
|
||||
#include <phoneaudioclient.h>
|
||||
#endif
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) {
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
// Did I say that I hate pointlessly asynchronous things?
|
||||
HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
ActivationHandler activationHandler(event);
|
||||
IActivateAudioInterfaceAsyncOperation* actHandler;
|
||||
HRESULT cr = ActivateAudioInterfaceAsync(devID, __uuidof(IAudioClient2), NULL, (IActivateAudioInterfaceCompletionHandler*)&activationHandler, &actHandler);
|
||||
if (callRes)
|
||||
*callRes = cr;
|
||||
DWORD resulttt = WaitForSingleObjectEx(event, INFINITE, false);
|
||||
DWORD last = GetLastError();
|
||||
CloseHandle(event);
|
||||
if (actRes)
|
||||
*actRes = activationHandler.actResult;
|
||||
return activationHandler.client;
|
||||
#else
|
||||
IAudioClient2* client;
|
||||
HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client);
|
||||
if(callRes)
|
||||
*callRes=S_OK;
|
||||
if(actRes)
|
||||
*actRes=res;
|
||||
return client;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
ActivationHandler::ActivationHandler(HANDLE _event) : event(_event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation * operation)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
HRESULT hrActivateResult = S_OK;
|
||||
IUnknown *punkAudioInterface = nullptr;
|
||||
|
||||
hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface);
|
||||
if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult))
|
||||
{
|
||||
punkAudioInterface->QueryInterface(IID_PPV_ARGS(&client));
|
||||
}
|
||||
|
||||
SetEvent(event);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,42 +1,42 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
#define LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
#include <mmdeviceapi.h>
|
||||
#endif
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace tgvoip {
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
class ActivationHandler :
|
||||
public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler >
|
||||
{
|
||||
public:
|
||||
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
|
||||
ActivationHandler(HANDLE _event);
|
||||
HANDLE event;
|
||||
IAudioClient2* client;
|
||||
HRESULT actResult;
|
||||
};
|
||||
#endif
|
||||
|
||||
class WindowsSandboxUtils {
|
||||
public:
|
||||
static IAudioClient2* ActivateAudioDevice(const wchar_t* devID, HRESULT* callResult, HRESULT* actResult);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
#define LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
#include <mmdeviceapi.h>
|
||||
#endif
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace tgvoip {
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
class ActivationHandler :
|
||||
public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler >
|
||||
{
|
||||
public:
|
||||
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
|
||||
ActivationHandler(HANDLE _event);
|
||||
HANDLE event;
|
||||
IAudioClient2* client;
|
||||
HRESULT actResult;
|
||||
};
|
||||
#endif
|
||||
|
||||
class WindowsSandboxUtils {
|
||||
public:
|
||||
static IAudioClient2* ActivateAudioDevice(const wchar_t* devID, HRESULT* callResult, HRESULT* actResult);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "WindowsSpecific.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
|
||||
std::string WindowsSpecific::GetErrorMessage(DWORD code){
|
||||
char buf[1024]={0};
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buf, sizeof(buf), NULL);
|
||||
return std::string(buf);
|
||||
#include "WindowsSpecific.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
|
||||
std::string WindowsSpecific::GetErrorMessage(DWORD code){
|
||||
char buf[1024]={0};
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buf, sizeof(buf), NULL);
|
||||
return std::string(buf);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,9 +4,14 @@
|
|||
@com.google.android.gms.common.annotation.KeepName *;
|
||||
}
|
||||
-keep class org.telegram.** { *; }
|
||||
-keep class com.google.android.exoplayer2.decoder.** { *; }
|
||||
-keep class com.google.android.exoplayer2.ext.** { *; }
|
||||
-keep class com.google.android.exoplayer2.util.** { *; }
|
||||
-keep class com.google.android.exoplayer2.metadata.** { *; }
|
||||
-keep class com.v2ray.ang.dto.** { *; }
|
||||
|
||||
-keep class io.github.trojan_gfw.** { *; }
|
||||
|
||||
-dontwarn com.coremedia.**
|
||||
-dontwarn org.telegram.**
|
||||
-dontwarn com.google.android.exoplayer2.ext.**
|
||||
|
@ -16,8 +21,9 @@
|
|||
-dontwarn com.google.common.cache.**
|
||||
-dontwarn com.google.common.primitives.**
|
||||
-dontwarn com.googlecode.mp4parser.**
|
||||
|
||||
# Use -keep to explicitly keep any other classes shrinking would remove
|
||||
-dontoptimize
|
||||
# -dontoptimize
|
||||
-dontobfuscate
|
||||
|
||||
# https://github.com/osmdroid/osmdroid/issues/633
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
|
||||
|
||||
<application>
|
||||
|
||||
<service android:name=".GcmPushListenerService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,77 @@
|
|||
package tw.nekomimi.nekogram;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
|
||||
import org.telegram.messenger.GcmPushListenerService;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.BuildVars;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.SharedConfig;
|
||||
import org.telegram.messenger.Utilities;
|
||||
|
||||
public class GcmImpl implements ExternalGcm.Interface {
|
||||
|
||||
public static boolean hasPlayServices;
|
||||
|
||||
@Override
|
||||
public void initPlayServices() {
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (hasPlayServices = checkPlayServices()) {
|
||||
final String currentPushString = SharedConfig.pushString;
|
||||
if (!TextUtils.isEmpty(currentPushString)) {
|
||||
if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) {
|
||||
FileLog.d("GCM regId = " + currentPushString);
|
||||
}
|
||||
} else {
|
||||
if (BuildVars.LOGS_ENABLED) {
|
||||
FileLog.d("GCM Registration not found.");
|
||||
}
|
||||
}
|
||||
Utilities.globalQueue.postRunnable(() -> {
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
String token = instanceIdResult.getToken();
|
||||
if (!TextUtils.isEmpty(token)) {
|
||||
GcmPushListenerService.sendRegistrationToServer(token);
|
||||
}
|
||||
}).addOnFailureListener(e -> {
|
||||
if (BuildVars.LOGS_ENABLED) {
|
||||
FileLog.d("Failed to get regid");
|
||||
}
|
||||
SharedConfig.pushStringStatus = "__FIREBASE_FAILED__";
|
||||
GcmPushListenerService.sendRegistrationToServer(null);
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (BuildVars.LOGS_ENABLED) {
|
||||
FileLog.d("No valid Google Play Services APK found.");
|
||||
}
|
||||
SharedConfig.pushStringStatus = "__NO_GOOGLE_PLAY_SERVICES__";
|
||||
GcmPushListenerService.sendRegistrationToServer(null);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private boolean checkPlayServices() {
|
||||
try {
|
||||
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ApplicationLoader.applicationContext);
|
||||
return resultCode == ConnectionResult.SUCCESS;
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRegistrationToServer() {
|
||||
GcmPushListenerService.sendRegistrationToServer(SharedConfig.pushString);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,16 @@
|
|||
android:smallScreens="true"
|
||||
android:xlargeScreens="true"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.LOCATION" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<permission android:name="${applicationId}.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.telephony" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.wifi" android:required="false" />
|
||||
|
@ -47,7 +57,6 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
|
||||
<uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
|
||||
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
|
||||
|
@ -65,21 +74,21 @@
|
|||
<uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_READ"/>
|
||||
<uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_WRITE"/>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name="org.telegram.messenger.${applicationClassName}"
|
||||
android:allowBackup="false"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:label="@string/Nekogram"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:largeHeap="true"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:manageSpaceActivity="org.telegram.ui.ExternalActionActivity"
|
||||
android:supportsRtl="false"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name="org.telegram.ui.LaunchActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
|
@ -142,6 +151,10 @@
|
|||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="tg" />
|
||||
<data android:scheme="vmess" />
|
||||
<data android:scheme="vmess1" />
|
||||
<data android:scheme="ss" />
|
||||
<data android:scheme="ssr" />
|
||||
</intent-filter>
|
||||
<intent-filter android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher" android:priority="1">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -196,6 +209,15 @@
|
|||
android:resizeableActivity="false"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".GoogleVoiceClientActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.voicesearch.SEND_MESSAGE_TO_CONTACTS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.telegram.ui.VoIPActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
|
@ -276,6 +298,14 @@
|
|||
<service android:name=".BringAppForegroundService" android:enabled="true"/>
|
||||
<service android:name=".NotificationsService" android:enabled="true"/>
|
||||
<service android:name=".NotificationRepeat" android:exported="false"/>
|
||||
<service android:name="tw.nekomimi.nekogram.NekoXPushService"
|
||||
android:enabled="true"
|
||||
android:label="@string/NekoXPushService"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".VideoEncodingService" android:enabled="true"/>
|
||||
<service android:name=".LocationSharingService" android:enabled="true"/>
|
||||
<service android:name=".voip.VoIPService" android:enabled="true"/>
|
||||
|
@ -367,10 +397,6 @@
|
|||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.5" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/notification" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -1,237 +1,237 @@
|
|||
1876;JM;Jamaica;XXX XXXX
|
||||
1869;KN;Saint Kitts & Nevis;XXX XXXX
|
||||
1868;TT;Trinidad & Tobago;XXX XXXX
|
||||
1784;VC;Saint Vincent & the Grenadines;XXX XXXX
|
||||
1767;DM;Dominica;XXX XXXX
|
||||
1758;LC;Saint Lucia;XXX XXXX
|
||||
1721;SX;Sint Maarten;XXX XXXX
|
||||
1684;AS;American Samoa;XXX XXXX
|
||||
1671;GU;Guam;XXX XXXX
|
||||
1670;MP;Northern Mariana Islands;XXX XXXX
|
||||
1664;MS;Montserrat;XXX XXXX
|
||||
1649;TC;Turks & Caicos Islands;XXX XXXX
|
||||
1473;GD;Grenada;XXX XXXX
|
||||
1441;BM;Bermuda;XXX XXXX
|
||||
1345;KY;Cayman Islands;XXX XXXX
|
||||
1340;VI;US Virgin Islands;XXX XXXX
|
||||
1284;VG;British Virgin Islands;XXX XXXX
|
||||
1268;AG;Antigua & Barbuda;XXX XXXX
|
||||
1264;AI;Anguilla;XXX XXXX
|
||||
1246;BB;Barbados;XXX XXXX
|
||||
1242;BS;Bahamas;XXX XXXX
|
||||
998;UZ;Uzbekistan;XX XXXXXXX
|
||||
996;KG;Kyrgyzstan;XXX XXXXXX
|
||||
995;GE;Georgia;XXX XXX XXX
|
||||
994;AZ;Azerbaijan;XX XXX XXXX
|
||||
993;TM;Turkmenistan;XX XXXXXX
|
||||
992;TJ;Tajikistan;XX XXX XXXX
|
||||
977;NP;Nepal;XX XXXX XXXX
|
||||
976;MN;Mongolia;XX XX XXXX
|
||||
975;BT;Bhutan;XX XXX XXX
|
||||
974;QA;Qatar;XX XXX XXX
|
||||
973;BH;Bahrain;XXXX XXXX
|
||||
972;IL;Israel;XX XXX XXXX
|
||||
971;AE;United Arab Emirates;XX XXX XXXX
|
||||
970;PS;Palestine;XXX XX XXXX
|
||||
968;OM;Oman;XXXX XXXX
|
||||
967;YE;Yemen;XXX XXX XXX
|
||||
966;SA;Saudi Arabia;XX XXX XXXX
|
||||
965;KW;Kuwait;XXXX XXXX
|
||||
964;IQ;Iraq;XXX XXX XXXX
|
||||
963;SY;Syria;XXX XXX XXX
|
||||
962;JO;Jordan;X XXXX XXXX
|
||||
961;LB;Lebanon
|
||||
960;MV;Maldives;XXX XXXX
|
||||
886;TW;Taiwan;XXX XXX XXX
|
||||
883;GO;International Networks
|
||||
882;GO;International Networks
|
||||
881;GO;Global Mobile Satellite
|
||||
880;BD;Bangladesh
|
||||
856;LA;Laos;XX XX XXX XXX
|
||||
855;KH;Cambodia
|
||||
853;MO;Macau;XXXX XXXX
|
||||
852;HK;Hong Kong;X XXX XXXX
|
||||
850;KP;North Korea
|
||||
692;MH;Marshall Islands
|
||||
691;FM;Micronesia
|
||||
690;TK;Tokelau
|
||||
689;PF;French Polynesia
|
||||
688;TV;Tuvalu
|
||||
687;NC;New Caledonia
|
||||
686;KI;Kiribati
|
||||
685;WS;Samoa
|
||||
683;NU;Niue
|
||||
682;CK;Cook Islands
|
||||
681;WF;Wallis & Futuna
|
||||
680;PW;Palau
|
||||
679;FJ;Fiji
|
||||
678;VU;Vanuatu
|
||||
677;SB;Solomon Islands
|
||||
676;TO;Tonga
|
||||
675;PG;Papua New Guinea
|
||||
674;NR;Nauru
|
||||
673;BN;Brunei Darussalam;XXX XXXX
|
||||
672;NF;Norfolk Island
|
||||
670;TL;Timor-Leste
|
||||
599;BQ;Bonaire, Sint Eustatius & Saba
|
||||
599;CW;Curaçao
|
||||
598;UY;Uruguay;X XXX XXXX
|
||||
597;SR;Suriname;XXX XXXX
|
||||
596;MQ;Martinique
|
||||
595;PY;Paraguay;XXX XXX XXX
|
||||
594;GF;French Guiana
|
||||
593;EC;Ecuador;XX XXX XXXX
|
||||
592;GY;Guyana
|
||||
591;BO;Bolivia;X XXX XXXX
|
||||
590;GP;Guadeloupe;XXX XX XX XX
|
||||
509;HT;Haiti
|
||||
508;PM;Saint Pierre & Miquelon
|
||||
507;PA;Panama;XXXX XXXX
|
||||
506;CR;Costa Rica;XXXX XXXX
|
||||
505;NI;Nicaragua;XXXX XXXX
|
||||
504;HN;Honduras;XXXX XXXX
|
||||
503;SV;El Salvador;XXXX XXXX
|
||||
502;GT;Guatemala;X XXX XXXX
|
||||
501;BZ;Belize
|
||||
500;FK;Falkland Islands
|
||||
423;LI;Liechtenstein
|
||||
421;SK;Slovakia;XXX XXX XXX
|
||||
420;CZ;Czech Republic;XXX XXX XXX
|
||||
389;MK;Macedonia;XX XXX XXX
|
||||
387;BA;Bosnia & Herzegovina;XX XXX XXX
|
||||
386;SI;Slovenia;XX XXX XXX
|
||||
385;HR;Croatia
|
||||
383;XK;Kosovo;XXXX XXXX
|
||||
382;ME;Montenegro
|
||||
381;RS;Serbia;XX XXX XXXX
|
||||
380;UA;Ukraine;XX XXX XX XX
|
||||
378;SM;San Marino;XXX XXX XXXX
|
||||
377;MC;Monaco;XXXX XXXX
|
||||
376;AD;Andorra;XX XX XX
|
||||
375;BY;Belarus;XX XXX XXXX
|
||||
374;AM;Armenia;XX XXX XXX
|
||||
373;MD;Moldova;XX XXX XXX
|
||||
372;EE;Estonia
|
||||
371;LV;Latvia;XXX XXXXX
|
||||
370;LT;Lithuania;XXX XXXXX
|
||||
359;BG;Bulgaria
|
||||
358;FI;Finland
|
||||
357;CY;Cyprus;XXXX XXXX
|
||||
356;MT;Malta;XX XX XX XX
|
||||
355;AL;Albania;XX XXX XXXX
|
||||
354;IS;Iceland;XXX XXXX
|
||||
353;IE;Ireland;XX XXX XXXX
|
||||
352;LU;Luxembourg
|
||||
351;PT;Portugal;X XXXX XXXX
|
||||
350;GI;Gibraltar;XXXX XXXX
|
||||
299;GL;Greenland;XXX XXX
|
||||
298;FO;Faroe Islands;XXX XXX
|
||||
297;AW;Aruba;XXX XXXX
|
||||
291;ER;Eritrea;X XXX XXX
|
||||
290;SH;Saint Helena;XX XXX
|
||||
269;KM;Comoros;XXX XXXX
|
||||
268;SZ;Swaziland;XXXX XXXX
|
||||
267;BW;Botswana;XX XXX XXX
|
||||
266;LS;Lesotho;XX XXX XXX
|
||||
265;MW;Malawi;77 XXX XXXX
|
||||
264;NA;Namibia;XX XXX XXXX
|
||||
263;ZW;Zimbabwe;XX XXX XXXX
|
||||
262;RE;Réunion;XXX XXX XXX
|
||||
261;MG;Madagascar;XX XX XXX XX
|
||||
260;ZM;Zambia;XX XXX XXXX
|
||||
258;MZ;Mozambique;XX XXX XXXX
|
||||
257;BI;Burundi;XX XX XXXX
|
||||
256;UG;Uganda;XX XXX XXXX
|
||||
255;TZ;Tanzania;XX XXX XXXX
|
||||
254;KE;Kenya;XXX XXX XXX
|
||||
253;DJ;Djibouti;XX XX XX XX
|
||||
252;SO;Somalia;XX XXX XXX
|
||||
251;ET;Ethiopia;XX XXX XXXX
|
||||
250;RW;Rwanda;XXX XXX XXX
|
||||
249;SD;Sudan;XX XXX XXXX
|
||||
248;SC;Seychelles;X XX XX XX
|
||||
247;SH;Saint Helena;XXXX
|
||||
246;IO;Diego Garcia;XXX XXXX
|
||||
245;GW;Guinea-Bissau;XXX XXXX
|
||||
244;AO;Angola;XXX XXX XXX
|
||||
243;CD;Congo (Dem. Rep.);XX XXX XXXX
|
||||
242;CG;Congo (Rep.);XX XXX XXXX
|
||||
241;GA;Gabon;X XX XX XX
|
||||
240;GQ;Equatorial Guinea;XXX XXX XXX
|
||||
239;ST;São Tomé & Príncipe;XX XXXXX
|
||||
238;CV;Cape Verde;XXX XXXX
|
||||
237;CM;Cameroon;XXXX XXXX
|
||||
236;CF;Central African Rep.;XX XX XX XX
|
||||
235;TD;Chad;XX XX XX XX
|
||||
234;NG;Nigeria
|
||||
233;GH;Ghana
|
||||
232;SL;Sierra Leone;XX XXX XXX
|
||||
231;LR;Liberia
|
||||
230;MU;Mauritius
|
||||
229;BJ;Benin;XX XXX XXX
|
||||
228;TG;Togo;XX XXX XXX
|
||||
227;NE;Niger;XX XX XX XX
|
||||
226;BF;Burkina Faso;XX XX XX XX
|
||||
225;CI;Côte d`Ivoire;XX XXX XXX
|
||||
224;GN;Guinea;XXX XXX XXX
|
||||
223;ML;Mali;XXXX XXXX
|
||||
222;MR;Mauritania;XXXX XXXX
|
||||
221;SN;Senegal;XX XXX XXXX
|
||||
220;GM;Gambia;XXX XXXX
|
||||
218;LY;Libya;XX XXX XXXX
|
||||
216;TN;Tunisia;XX XXX XXX
|
||||
213;DZ;Algeria;XXX XX XX XX
|
||||
212;MA;Morocco;XX XXX XXXX
|
||||
211;SS;South Sudan;XX XXX XXXX
|
||||
98;IR;Iran;XXX XXX XXXX
|
||||
95;MM;Myanmar
|
||||
94;LK;Sri Lanka;XX XXX XXXX
|
||||
93;AF;Afghanistan;XXX XXX XXX
|
||||
92;PK;Pakistan;XXX XXX XXXX
|
||||
91;IN;India;XXXXX XXXXX
|
||||
90;TR;Turkey;XXX XXX XXXX
|
||||
86;CN;China;XXX XXXX XXXX
|
||||
84;VN;Vietnam
|
||||
82;KR;South Korea
|
||||
81;JP;Japan;XX XXXX XXXX
|
||||
66;TH;Thailand;X XXXX XXXX
|
||||
65;SG;Singapore;XXXX XXXX
|
||||
64;NZ;New Zealand
|
||||
63;PH;Philippines;XXX XXX XXXX
|
||||
62;ID;Indonesia
|
||||
61;AU;Australia;XXX XXX XXX
|
||||
60;MY;Malaysia
|
||||
58;VE;Venezuela;XXX XXX XXXX
|
||||
57;CO;Colombia;XXX XXX XXXX
|
||||
56;CL;Chile;X XXXX XXXX
|
||||
55;BR;Brazil;XX XXXXX XXXX
|
||||
54;AR;Argentina
|
||||
53;CU;Cuba;XXXX XXXX
|
||||
52;MX;Mexico
|
||||
51;PE;Peru;XXX XXX XXX
|
||||
49;DE;Germany
|
||||
48;PL;Poland;XXX XXX XXX
|
||||
47;NO;Norway;XXXX XXXX
|
||||
46;SE;Sweden;XX XXX XXXX
|
||||
45;DK;Denmark;XXXX XXXX
|
||||
44;GB;United Kingdom;XXXX XXXXXX
|
||||
43;AT;Austria
|
||||
42;YL;Y-land
|
||||
41;CH;Switzerland;XX XXX XXXX
|
||||
40;RO;Romania;XXX XXX XXX
|
||||
39;IT;Italy
|
||||
36;HU;Hungary;XXX XXX XXX
|
||||
34;ES;Spain;XXX XXX XXX
|
||||
33;FR;France;X XX XX XX XX
|
||||
32;BE;Belgium;XXX XX XX XX
|
||||
31;NL;Netherlands;X XX XX XX XX
|
||||
30;GR;Greece;XXX XXX XXXX
|
||||
27;ZA;South Africa;XX XXX XXXX
|
||||
20;EG;Egypt;XX XXXX XXXX
|
||||
7;KZ;Kazakhstan;XXX XXX XX XX
|
||||
7;RU;Russian Federation;XXX XXX XXXX
|
||||
1;PR;Puerto Rico;XXX XXX XXXX
|
||||
1;DO;Dominican Rep.;XXX XXX XXXX
|
||||
1;CA;Canada;XXX XXX XXXX
|
||||
1;US;USA;XXX XXX XXXX
|
||||
1876;JM;Jamaica;XXX XXXX
|
||||
1869;KN;Saint Kitts & Nevis;XXX XXXX
|
||||
1868;TT;Trinidad & Tobago;XXX XXXX
|
||||
1784;VC;Saint Vincent & the Grenadines;XXX XXXX
|
||||
1767;DM;Dominica;XXX XXXX
|
||||
1758;LC;Saint Lucia;XXX XXXX
|
||||
1721;SX;Sint Maarten;XXX XXXX
|
||||
1684;AS;American Samoa;XXX XXXX
|
||||
1671;GU;Guam;XXX XXXX
|
||||
1670;MP;Northern Mariana Islands;XXX XXXX
|
||||
1664;MS;Montserrat;XXX XXXX
|
||||
1649;TC;Turks & Caicos Islands;XXX XXXX
|
||||
1473;GD;Grenada;XXX XXXX
|
||||
1441;BM;Bermuda;XXX XXXX
|
||||
1345;KY;Cayman Islands;XXX XXXX
|
||||
1340;VI;US Virgin Islands;XXX XXXX
|
||||
1284;VG;British Virgin Islands;XXX XXXX
|
||||
1268;AG;Antigua & Barbuda;XXX XXXX
|
||||
1264;AI;Anguilla;XXX XXXX
|
||||
1246;BB;Barbados;XXX XXXX
|
||||
1242;BS;Bahamas;XXX XXXX
|
||||
998;UZ;Uzbekistan;XX XXXXXXX
|
||||
996;KG;Kyrgyzstan;XXX XXXXXX
|
||||
995;GE;Georgia;XXX XXX XXX
|
||||
994;AZ;Azerbaijan;XX XXX XXXX
|
||||
993;TM;Turkmenistan;XX XXXXXX
|
||||
992;TJ;Tajikistan;XX XXX XXXX
|
||||
977;NP;Nepal;XX XXXX XXXX
|
||||
976;MN;Mongolia;XX XX XXXX
|
||||
975;BT;Bhutan;XX XXX XXX
|
||||
974;QA;Qatar;XX XXX XXX
|
||||
973;BH;Bahrain;XXXX XXXX
|
||||
972;IL;Israel;XX XXX XXXX
|
||||
971;AE;United Arab Emirates;XX XXX XXXX
|
||||
970;PS;Palestine;XXX XX XXXX
|
||||
968;OM;Oman;XXXX XXXX
|
||||
967;YE;Yemen;XXX XXX XXX
|
||||
966;SA;Saudi Arabia;XX XXX XXXX
|
||||
965;KW;Kuwait;XXXX XXXX
|
||||
964;IQ;Iraq;XXX XXX XXXX
|
||||
963;SY;Syria;XXX XXX XXX
|
||||
962;JO;Jordan;X XXXX XXXX
|
||||
961;LB;Lebanon
|
||||
960;MV;Maldives;XXX XXXX
|
||||
886;TW;Taiwan;XXX XXX XXX
|
||||
883;GO;International Networks
|
||||
882;GO;International Networks
|
||||
881;GO;Global Mobile Satellite
|
||||
880;BD;Bangladesh
|
||||
856;LA;Laos;XX XX XXX XXX
|
||||
855;KH;Cambodia
|
||||
853;MO;Macau;XXXX XXXX
|
||||
852;HK;Hong Kong;X XXX XXXX
|
||||
850;KP;North Korea
|
||||
692;MH;Marshall Islands
|
||||
691;FM;Micronesia
|
||||
690;TK;Tokelau
|
||||
689;PF;French Polynesia
|
||||
688;TV;Tuvalu
|
||||
687;NC;New Caledonia
|
||||
686;KI;Kiribati
|
||||
685;WS;Samoa
|
||||
683;NU;Niue
|
||||
682;CK;Cook Islands
|
||||
681;WF;Wallis & Futuna
|
||||
680;PW;Palau
|
||||
679;FJ;Fiji
|
||||
678;VU;Vanuatu
|
||||
677;SB;Solomon Islands
|
||||
676;TO;Tonga
|
||||
675;PG;Papua New Guinea
|
||||
674;NR;Nauru
|
||||
673;BN;Brunei Darussalam;XXX XXXX
|
||||
672;NF;Norfolk Island
|
||||
670;TL;Timor-Leste
|
||||
599;BQ;Bonaire, Sint Eustatius & Saba
|
||||
599;CW;Curaçao
|
||||
598;UY;Uruguay;X XXX XXXX
|
||||
597;SR;Suriname;XXX XXXX
|
||||
596;MQ;Martinique
|
||||
595;PY;Paraguay;XXX XXX XXX
|
||||
594;GF;French Guiana
|
||||
593;EC;Ecuador;XX XXX XXXX
|
||||
592;GY;Guyana
|
||||
591;BO;Bolivia;X XXX XXXX
|
||||
590;GP;Guadeloupe;XXX XX XX XX
|
||||
509;HT;Haiti
|
||||
508;PM;Saint Pierre & Miquelon
|
||||
507;PA;Panama;XXXX XXXX
|
||||
506;CR;Costa Rica;XXXX XXXX
|
||||
505;NI;Nicaragua;XXXX XXXX
|
||||
504;HN;Honduras;XXXX XXXX
|
||||
503;SV;El Salvador;XXXX XXXX
|
||||
502;GT;Guatemala;X XXX XXXX
|
||||
501;BZ;Belize
|
||||
500;FK;Falkland Islands
|
||||
423;LI;Liechtenstein
|
||||
421;SK;Slovakia;XXX XXX XXX
|
||||
420;CZ;Czech Republic;XXX XXX XXX
|
||||
389;MK;Macedonia;XX XXX XXX
|
||||
387;BA;Bosnia & Herzegovina;XX XXX XXX
|
||||
386;SI;Slovenia;XX XXX XXX
|
||||
385;HR;Croatia
|
||||
383;XK;Kosovo;XXXX XXXX
|
||||
382;ME;Montenegro
|
||||
381;RS;Serbia;XX XXX XXXX
|
||||
380;UA;Ukraine;XX XXX XX XX
|
||||
378;SM;San Marino;XXX XXX XXXX
|
||||
377;MC;Monaco;XXXX XXXX
|
||||
376;AD;Andorra;XX XX XX
|
||||
375;BY;Belarus;XX XXX XXXX
|
||||
374;AM;Armenia;XX XXX XXX
|
||||
373;MD;Moldova;XX XXX XXX
|
||||
372;EE;Estonia
|
||||
371;LV;Latvia;XXX XXXXX
|
||||
370;LT;Lithuania;XXX XXXXX
|
||||
359;BG;Bulgaria
|
||||
358;FI;Finland
|
||||
357;CY;Cyprus;XXXX XXXX
|
||||
356;MT;Malta;XX XX XX XX
|
||||
355;AL;Albania;XX XXX XXXX
|
||||
354;IS;Iceland;XXX XXXX
|
||||
353;IE;Ireland;XX XXX XXXX
|
||||
352;LU;Luxembourg
|
||||
351;PT;Portugal;X XXXX XXXX
|
||||
350;GI;Gibraltar;XXXX XXXX
|
||||
299;GL;Greenland;XXX XXX
|
||||
298;FO;Faroe Islands;XXX XXX
|
||||
297;AW;Aruba;XXX XXXX
|
||||
291;ER;Eritrea;X XXX XXX
|
||||
290;SH;Saint Helena;XX XXX
|
||||
269;KM;Comoros;XXX XXXX
|
||||
268;SZ;Swaziland;XXXX XXXX
|
||||
267;BW;Botswana;XX XXX XXX
|
||||
266;LS;Lesotho;XX XXX XXX
|
||||
265;MW;Malawi;77 XXX XXXX
|
||||
264;NA;Namibia;XX XXX XXXX
|
||||
263;ZW;Zimbabwe;XX XXX XXXX
|
||||
262;RE;Réunion;XXX XXX XXX
|
||||
261;MG;Madagascar;XX XX XXX XX
|
||||
260;ZM;Zambia;XX XXX XXXX
|
||||
258;MZ;Mozambique;XX XXX XXXX
|
||||
257;BI;Burundi;XX XX XXXX
|
||||
256;UG;Uganda;XX XXX XXXX
|
||||
255;TZ;Tanzania;XX XXX XXXX
|
||||
254;KE;Kenya;XXX XXX XXX
|
||||
253;DJ;Djibouti;XX XX XX XX
|
||||
252;SO;Somalia;XX XXX XXX
|
||||
251;ET;Ethiopia;XX XXX XXXX
|
||||
250;RW;Rwanda;XXX XXX XXX
|
||||
249;SD;Sudan;XX XXX XXXX
|
||||
248;SC;Seychelles;X XX XX XX
|
||||
247;SH;Saint Helena;XXXX
|
||||
246;IO;Diego Garcia;XXX XXXX
|
||||
245;GW;Guinea-Bissau;XXX XXXX
|
||||
244;AO;Angola;XXX XXX XXX
|
||||
243;CD;Congo (Dem. Rep.);XX XXX XXXX
|
||||
242;CG;Congo (Rep.);XX XXX XXXX
|
||||
241;GA;Gabon;X XX XX XX
|
||||
240;GQ;Equatorial Guinea;XXX XXX XXX
|
||||
239;ST;São Tomé & Príncipe;XX XXXXX
|
||||
238;CV;Cape Verde;XXX XXXX
|
||||
237;CM;Cameroon;XXXX XXXX
|
||||
236;CF;Central African Rep.;XX XX XX XX
|
||||
235;TD;Chad;XX XX XX XX
|
||||
234;NG;Nigeria
|
||||
233;GH;Ghana
|
||||
232;SL;Sierra Leone;XX XXX XXX
|
||||
231;LR;Liberia
|
||||
230;MU;Mauritius
|
||||
229;BJ;Benin;XX XXX XXX
|
||||
228;TG;Togo;XX XXX XXX
|
||||
227;NE;Niger;XX XX XX XX
|
||||
226;BF;Burkina Faso;XX XX XX XX
|
||||
225;CI;Côte d`Ivoire;XX XXX XXX
|
||||
224;GN;Guinea;XXX XXX XXX
|
||||
223;ML;Mali;XXXX XXXX
|
||||
222;MR;Mauritania;XXXX XXXX
|
||||
221;SN;Senegal;XX XXX XXXX
|
||||
220;GM;Gambia;XXX XXXX
|
||||
218;LY;Libya;XX XXX XXXX
|
||||
216;TN;Tunisia;XX XXX XXX
|
||||
213;DZ;Algeria;XXX XX XX XX
|
||||
212;MA;Morocco;XX XXX XXXX
|
||||
211;SS;South Sudan;XX XXX XXXX
|
||||
98;IR;Iran;XXX XXX XXXX
|
||||
95;MM;Myanmar
|
||||
94;LK;Sri Lanka;XX XXX XXXX
|
||||
93;AF;Afghanistan;XXX XXX XXX
|
||||
92;PK;Pakistan;XXX XXX XXXX
|
||||
91;IN;India;XXXXX XXXXX
|
||||
90;TR;Turkey;XXX XXX XXXX
|
||||
86;CN;China;XXX XXXX XXXX
|
||||
84;VN;Vietnam
|
||||
82;KR;South Korea
|
||||
81;JP;Japan;XX XXXX XXXX
|
||||
66;TH;Thailand;X XXXX XXXX
|
||||
65;SG;Singapore;XXXX XXXX
|
||||
64;NZ;New Zealand
|
||||
63;PH;Philippines;XXX XXX XXXX
|
||||
62;ID;Indonesia
|
||||
61;AU;Australia;XXX XXX XXX
|
||||
60;MY;Malaysia
|
||||
58;VE;Venezuela;XXX XXX XXXX
|
||||
57;CO;Colombia;XXX XXX XXXX
|
||||
56;CL;Chile;X XXXX XXXX
|
||||
55;BR;Brazil;XX XXXXX XXXX
|
||||
54;AR;Argentina
|
||||
53;CU;Cuba;XXXX XXXX
|
||||
52;MX;Mexico
|
||||
51;PE;Peru;XXX XXX XXX
|
||||
49;DE;Germany
|
||||
48;PL;Poland;XXX XXX XXX
|
||||
47;NO;Norway;XXXX XXXX
|
||||
46;SE;Sweden;XX XXX XXXX
|
||||
45;DK;Denmark;XXXX XXXX
|
||||
44;GB;United Kingdom;XXXX XXXXXX
|
||||
43;AT;Austria
|
||||
42;YL;Y-land
|
||||
41;CH;Switzerland;XX XXX XXXX
|
||||
40;RO;Romania;XXX XXX XXX
|
||||
39;IT;Italy
|
||||
36;HU;Hungary;XXX XXX XXX
|
||||
34;ES;Spain;XXX XXX XXX
|
||||
33;FR;France;X XX XX XX XX
|
||||
32;BE;Belgium;XXX XX XX XX
|
||||
31;NL;Netherlands;X XX XX XX XX
|
||||
30;GR;Greece;XXX XXX XXXX
|
||||
27;ZA;South Africa;XX XXX XXXX
|
||||
20;EG;Egypt;XX XXXX XXXX
|
||||
7;KZ;Kazakhstan;XXX XXX XX XX
|
||||
7;RU;Russian Federation;XXX XXX XXXX
|
||||
1;PR;Puerto Rico;XXX XXX XXXX
|
||||
1;DO;Dominican Rep.;XXX XXX XXXX
|
||||
1;CA;Canada;XXX XXX XXXX
|
||||
1;US;USA;XXX XXX XXXX
|
||||
999;TG;Test Number;XX X XXXX
|
|
@ -0,0 +1,441 @@
|
|||
windowBackgroundGray=#eeeeee
|
||||
chats_tabletSelectedOverlay=#eeeeee
|
||||
login_progressInner=#eeeeee
|
||||
contextProgressInner1=#eeeeee
|
||||
chat_emojiPanelShadowLine=#eeeeee
|
||||
chat_emojiSearchBackground=#eeeeee
|
||||
windowBackgroundWhite=-328966
|
||||
dialogBackground=-328966
|
||||
chats_menuBackground=-328966
|
||||
files_folderIcon=-328966
|
||||
graySection=-328966
|
||||
groupcreate_checkboxCheck=-328966
|
||||
chat_topPanelBackground=-328966
|
||||
chat_unreadMessagesStartBackground=-328966
|
||||
chat_emojiPanelBackground=-328966
|
||||
player_background=-328966
|
||||
windowBackgroundWhiteBlackText=#424242
|
||||
actionBarDefaultSubmenuItem=#424242
|
||||
inappPlayerPerformer=#424242
|
||||
inappPlayerTitle=#424242
|
||||
dialogTextBlack=#424242
|
||||
chats_name=#424242
|
||||
chats_nameIcon=#424242
|
||||
chats_menuItemText=#424242
|
||||
contextProgressInner3=#424242
|
||||
chat_messagePanelText=#424242
|
||||
chat_messageTextIn=#424242
|
||||
chat_replyPanelMessage=#424242
|
||||
chat_searchPanelText=#424242
|
||||
chat_emojiPanelTrendingTitle=#424242
|
||||
chat_inReplyMessageText=#424242
|
||||
windowBackgroundWhiteInputField=#9e9e9e
|
||||
windowBackgroundWhiteGrayText=#9e9e9e
|
||||
windowBackgroundWhiteGrayText2=#9e9e9e
|
||||
windowBackgroundWhiteGrayText3=#9e9e9e
|
||||
windowBackgroundWhiteGrayText4=#9e9e9e
|
||||
windowBackgroundWhiteGrayText5=#9e9e9e
|
||||
windowBackgroundWhiteGrayText6=#9e9e9e
|
||||
windowBackgroundWhiteGrayText7=#9e9e9e
|
||||
windowBackgroundWhiteGrayText8=#9e9e9e
|
||||
windowBackgroundWhiteGrayIcon=#9e9e9e
|
||||
windowBackgroundWhiteHintText=#9e9e9e
|
||||
windowBackgroundWhiteGrayLine=#9e9e9e
|
||||
player_button=#9e9e9e
|
||||
sessions_devicesImage=#9e9e9e
|
||||
checkboxSquareUnchecked=#9e9e9e
|
||||
dialogIcon=#9e9e9e
|
||||
dialogCheckboxSquareUnchecked=#9e9e9e
|
||||
dialogInputField=#9e9e9e
|
||||
dialogRadioBackground=#9e9e9e
|
||||
dialogTextGray=#9e9e9e
|
||||
dialogTextGray2=#9e9e9e
|
||||
dialogTextGray3=#9e9e9e
|
||||
dialogTextGray4=#9e9e9e
|
||||
dialogTextHint=#9e9e9e
|
||||
calls_ratingStar=#9e9e9e
|
||||
chats_date=#9e9e9e
|
||||
chats_menuItemIcon=#9e9e9e
|
||||
chats_muteIcon=#9e9e9e
|
||||
chats_pinnedIcon=#9e9e9e
|
||||
emptyListPlaceholder=#9e9e9e
|
||||
fastScrollInactive=#9e9e9e
|
||||
groupcreate_hintText=#9e9e9e
|
||||
groupcreate_offlineText=#9e9e9e
|
||||
groupcreate_sectionText=#9e9e9e
|
||||
stickers_menu=#9e9e9e
|
||||
chat_messagePanelHint=#9e9e9e
|
||||
chat_messagePanelIcons=#9e9e9e
|
||||
chat_messagePanelVoiceDelete=#9e9e9e
|
||||
chat_recordTime=#9e9e9e
|
||||
chat_replyPanelClose=#9e9e9e
|
||||
chat_secretChatStatusText=#9e9e9e
|
||||
chat_topPanelClose=#9e9e9e
|
||||
chat_emojiPanelEmptyText=#9e9e9e
|
||||
chat_emojiPanelMasksIcon=#9e9e9e
|
||||
chat_emojiPanelTrendingDescription=#9e9e9e
|
||||
chat_topPanelMessage=#9e9e9e
|
||||
key_chat_messagePanelVoiceLock=#9e9e9e
|
||||
chat_emojiPanelStickerSetName=#9e9e9e
|
||||
chat_emojiPanelStickerSetNameIcon=#9e9e9e
|
||||
windowBackgroundWhiteRedText=-769226
|
||||
windowBackgroundWhiteRedText2=-769226
|
||||
windowBackgroundWhiteRedText3=-769226
|
||||
windowBackgroundWhiteRedText4=-769226
|
||||
windowBackgroundWhiteRedText5=-769226
|
||||
windowBackgroundWhiteRedText6=-769226
|
||||
dialogTextRed=-769226
|
||||
calls_callReceivedRedIcon=-769226
|
||||
chats_sentError=-769226
|
||||
featuredStickers_delButton=-769226
|
||||
chat_sentError=-769226
|
||||
chat_reportSpam=-769226
|
||||
location_sendLiveLocationBackground=-769226
|
||||
divider=83886080
|
||||
actionBarActionModeDefaultIcon=-1
|
||||
groupcreate_spanText=-1
|
||||
profile_verifiedCheck=-1
|
||||
returnToCallBackground=-1
|
||||
chat_messageTextOut=-1
|
||||
chat_secretTimeText=-1
|
||||
chat_botKeyboardButtonText=-1
|
||||
chat_goDownButtonCounterBackground=-1
|
||||
chat_goDownButtonIcon=-1
|
||||
chat_mediaLoaderPhotoIconSelected=-1
|
||||
chat_outAudioSeekbarFill=-1
|
||||
chat_outAudioTitleText=-1
|
||||
chat_outContactBackground=-1
|
||||
chat_outContactNameText=-1
|
||||
chat_outFileBackground=-1
|
||||
chat_outFileBackgroundSelected=-1
|
||||
chat_outFileNameText=-1
|
||||
chat_outInstant=-1
|
||||
chat_outLoader=-1
|
||||
chat_outLoaderPhotoIcon=-1
|
||||
chat_outLoaderPhotoIconSelected=-1
|
||||
chat_outLoaderSelected=-1
|
||||
chat_outLocationBackground=-1
|
||||
chat_outPreviewInstantText=-1
|
||||
chat_outPreviewLine=-1
|
||||
chat_outReplyLine=-1
|
||||
chat_outReplyNameText=-1
|
||||
chat_outSiteNameText=-1
|
||||
chat_outVenueNameText=-1
|
||||
chat_outVoiceSeekbarFill=-1
|
||||
chat_inAudioSelectedProgress=-1
|
||||
chat_inFileIcon=-1
|
||||
chat_inFileProgress=-1
|
||||
chat_inFileProgressSelected=-1
|
||||
chat_inFileSelectedIcon=-1
|
||||
chat_inLoaderPhoto=-1
|
||||
chat_inLoaderPhotoSelected=-1
|
||||
chat_inLocationIcon=-1
|
||||
actionBarActionModeDefaultTop=0
|
||||
chats_verifiedBackground=0
|
||||
profile_verifiedBackground=0
|
||||
actionBarActionModeDefaultSelector=#000000
|
||||
actionBarDefaultSearchPlaceholder=#80ffffff
|
||||
actionBarDefaultSelector=#80ffffff
|
||||
actionBarDefaultSubtitle=#80ffffff
|
||||
avatar_actionBarSelectorBlue=#80ffffff
|
||||
avatar_actionBarSelectorCyan=#80ffffff
|
||||
avatar_actionBarSelectorGreen=#80ffffff
|
||||
avatar_actionBarSelectorOrange=#80ffffff
|
||||
avatar_actionBarSelectorPink=#80ffffff
|
||||
avatar_actionBarSelectorRed=#80ffffff
|
||||
avatar_actionBarSelectorViolet=#80ffffff
|
||||
avatar_subtitleInProfileBlue=#80ffffff
|
||||
avatar_subtitleInProfileCyan=#80ffffff
|
||||
avatar_subtitleInProfileGreen=#80ffffff
|
||||
avatar_subtitleInProfileOrange=#80ffffff
|
||||
avatar_subtitleInProfilePink=#80ffffff
|
||||
avatar_subtitleInProfileRed=#80ffffff
|
||||
avatar_subtitleInProfileViolet=#80ffffff
|
||||
chats_menuPhone=#80ffffff
|
||||
chats_menuPhoneCats=#80ffffff
|
||||
contextProgressInner2=#80ffffff
|
||||
chat_recordedVoicePlayPausePressed=#80ffffff
|
||||
chat_recordedVoiceProgress=#80ffffff
|
||||
chat_muteIcon=#80ffffff
|
||||
chat_outAudioDurationSelectedText=#80ffffff
|
||||
chat_outAudioDurationText=#80ffffff
|
||||
chat_outForwardedNameText=#80ffffff
|
||||
chat_outInstantSelected=#80ffffff
|
||||
chat_outMenu=#80ffffff
|
||||
chat_outMenuSelected=#80ffffff
|
||||
chat_outPreviewInstantSelectedText=#80ffffff
|
||||
chat_outReplyMessageText=#80ffffff
|
||||
chat_outSentCheck=#80ffffff
|
||||
chat_outSentCheckSelected=#80ffffff
|
||||
chat_outSentClock=#80ffffff
|
||||
chat_outSentClockSelected=#80ffffff
|
||||
chat_outTimeSelectedText=#80ffffff
|
||||
chat_outTimeText=#80ffffff
|
||||
chat_outViaBotNameText=#80ffffff
|
||||
chat_outViews=#80ffffff
|
||||
chat_outViewsSelected=#80ffffff
|
||||
actionBarWhiteSelector=#40000000
|
||||
chats_actionPressedBackground=#40000000
|
||||
profile_actionPressedBackground=#40000000
|
||||
player_actionBarTop=855638016
|
||||
player_placeholder=#809e9e9e
|
||||
inappPlayerClose=#809e9e9e
|
||||
dialogLineProgressBackground=#809e9e9e
|
||||
picker_disabledButton=#809e9e9e
|
||||
radioBackground=#809e9e9e
|
||||
switchTrack=#809e9e9e
|
||||
chat_messagePanelCancelInlineBot=#809e9e9e
|
||||
player_progressBackground=#409e9e9e
|
||||
chat_wallpaper=-657931
|
||||
chats_pinnedOverlay=-657931
|
||||
switchThumb=-657931
|
||||
dialogBackgroundGray=-657931
|
||||
player_placeholderBackground=-657931
|
||||
listSelectorSDK21=#80000000
|
||||
dialogButtonSelector=#80000000
|
||||
chats_menuCloudBackgroundCats=#80000000
|
||||
chats_menuTopShadow=#80000000
|
||||
location_markerX=#80000000
|
||||
stickers_menuSelector=#80000000
|
||||
chat_serviceBackgroundSelected=#80000000
|
||||
chat_mediaLoaderPhoto=#80000000
|
||||
chat_mediaLoaderPhotoSelected=#80000000
|
||||
chat_mediaTimeBackground=#80000000
|
||||
dialogGrayLine=167772160
|
||||
chat_replyPanelLine=167772160
|
||||
chats_message=#757575
|
||||
featuredStickers_delButtonPressed=#80f44336
|
||||
chat_inBubbleSelected=#c0ffffff
|
||||
chat_outAudioPerfomerText=#c0ffffff
|
||||
chat_outContactPhoneText=#c0ffffff
|
||||
chat_outFileInfoSelectedText=#c0ffffff
|
||||
chat_outFileInfoText=#c0ffffff
|
||||
chat_outReplyMediaMessageSelectedText=#c0ffffff
|
||||
chat_outReplyMediaMessageText=#c0ffffff
|
||||
chat_outVenueInfoSelectedText=#c0ffffff
|
||||
chat_outVenueInfoText=#c0ffffff
|
||||
chat_messageLinkOut=#c0ffffff
|
||||
chat_inBubbleShadow=#000000
|
||||
chat_serviceBackground=#60000000
|
||||
chat_emojiPanelBackspace=#609e9e9e
|
||||
chat_emojiPanelIcon=#51000000
|
||||
chat_outAudioSeekbar=#60ffffff
|
||||
chat_outAudioSeekbarSelected=#60ffffff
|
||||
chat_outVoiceSeekbar=#60ffffff
|
||||
chat_outVoiceSeekbarSelected=#60ffffff
|
||||
windowBackgroundWhiteBlueHeader=#3f51b5
|
||||
windowBackgroundWhiteBlueText=#3f51b5
|
||||
windowBackgroundWhiteBlueText2=#3f51b5
|
||||
windowBackgroundWhiteBlueText3=#3f51b5
|
||||
windowBackgroundWhiteBlueText4=#3f51b5
|
||||
windowBackgroundWhiteBlueText5=#3f51b5
|
||||
windowBackgroundWhiteBlueText6=#3f51b5
|
||||
windowBackgroundWhiteBlueText7=#3f51b5
|
||||
windowBackgroundWhiteGreenText=#3f51b5
|
||||
windowBackgroundWhiteGreenText2=#3f51b5
|
||||
windowBackgroundWhiteValueText=#3f51b5
|
||||
windowBackgroundWhiteInputFieldActivated=#3f51b5
|
||||
windowBackgroundWhiteLinkText=#3f51b5
|
||||
actionBarActionModeDefault=#3f51b5
|
||||
actionBarDefault=#3f51b5
|
||||
player_actionBarItems=#3f51b5
|
||||
player_actionBarTitle=#3f51b5
|
||||
avatar_backgroundActionBarCyan=#3f51b5
|
||||
avatar_backgroundActionBarBlue=#3f51b5
|
||||
avatar_backgroundActionBarGreen=#3f51b5
|
||||
avatar_backgroundActionBarOrange=#3f51b5
|
||||
avatar_backgroundActionBarPink=#3f51b5
|
||||
avatar_backgroundActionBarViolet=#3f51b5
|
||||
avatar_backgroundActionBarRed=#3f51b5
|
||||
player_progress=#3f51b5
|
||||
player_buttonActive=#3f51b5
|
||||
player_time=#3f51b5
|
||||
avatar_backgroundBlue=#3f51b5
|
||||
avatar_backgroundCyan=#3f51b5
|
||||
avatar_backgroundGreen=#3f51b5
|
||||
avatar_backgroundOrange=#3f51b5
|
||||
avatar_backgroundInProfileBlue=#3f51b5
|
||||
avatar_backgroundInProfileCyan=#3f51b5
|
||||
avatar_backgroundInProfileGreen=#3f51b5
|
||||
avatar_backgroundInProfileOrange=#3f51b5
|
||||
avatar_backgroundInProfilePink=#3f51b5
|
||||
avatar_backgroundInProfileRed=#3f51b5
|
||||
avatar_backgroundInProfileViolet=#3f51b5
|
||||
avatar_backgroundPink=#3f51b5
|
||||
avatar_backgroundRed=#3f51b5
|
||||
avatar_backgroundViolet=#3f51b5
|
||||
avatar_nameInMessageBlue=#3f51b5
|
||||
avatar_nameInMessageCyan=#3f51b5
|
||||
avatar_nameInMessageGreen=#3f51b5
|
||||
avatar_nameInMessageOrange=#3f51b5
|
||||
avatar_nameInMessagePink=#3f51b5
|
||||
avatar_nameInMessageRed=#3f51b5
|
||||
avatar_nameInMessageViolet=#3f51b5
|
||||
inappPlayerPlayPause=#3f51b5
|
||||
checkboxSquareBackground=#3f51b5
|
||||
changephoneinfo_image=#3f51b5
|
||||
dialogBadgeBackground=#3f51b5
|
||||
dialogButton=#3f51b5
|
||||
dialogCheckboxSquareBackground=#3f51b5
|
||||
dialogInputFieldActivated=#3f51b5
|
||||
dialogLineProgress=#3f51b5
|
||||
dialogProgressCircle=#3f51b5
|
||||
dialogRadioBackgroundChecked=#3f51b5
|
||||
dialogRoundCheckBox=#3f51b5
|
||||
dialogScrollGlow=#3f51b5
|
||||
dialogTextBlue=#3f51b5
|
||||
dialogTextBlue2=#3f51b5
|
||||
dialogTextBlue3=#3f51b5
|
||||
dialogTextBlue4=#3f51b5
|
||||
dialogTextLink=#3f51b5
|
||||
calls_ratingStarSelected=#3f51b5
|
||||
calls_callReceivedGreenIcon=#3f51b5
|
||||
chats_actionBackground=#3f51b5
|
||||
chats_actionMessage=#3f51b5
|
||||
chats_attachMessage=#3f51b5
|
||||
chats_draft=#3f51b5
|
||||
chats_nameMessage=#3f51b5
|
||||
chats_secretIcon=#3f51b5
|
||||
chats_secretName=#3f51b5
|
||||
chats_sentCheck=#3f51b5
|
||||
chats_sentClock=#3f51b5
|
||||
chats_unreadCounter=#3f51b5
|
||||
chats_verifiedCheck=#3f51b5
|
||||
checkbox=#3f51b5
|
||||
fastScrollActive=#3f51b5
|
||||
featuredStickers_addButton=#3f51b5
|
||||
featuredStickers_addedIcon=#3f51b5
|
||||
featuredStickers_unread=#3f51b5
|
||||
files_folderIconBackground=#3f51b5
|
||||
groupcreate_checkbox=#3f51b5
|
||||
groupcreate_cursor=#3f51b5
|
||||
groupcreate_onlineText=#3f51b5
|
||||
avatar_backgroundGroupCreateSpanBlue=#3f51b5
|
||||
location_sendLocationBackground=#3f51b5
|
||||
login_progressOuter=#3f51b5
|
||||
contextProgressOuter1=#3f51b5
|
||||
musicPicker_buttonBackground=#3f51b5
|
||||
musicPicker_checkbox=#3f51b5
|
||||
picker_badge=#3f51b5
|
||||
picker_enabledButton=#3f51b5
|
||||
profile_actionIcon=#3f51b5
|
||||
profile_creatorIcon=#3f51b5
|
||||
progressCircle=#3f51b5
|
||||
radioBackgroundChecked=#3f51b5
|
||||
returnToCallText=#3f51b5
|
||||
sharedMedia_linkPlaceholder=#3f51b5
|
||||
sharedMedia_startStopLoadIcon=#3f51b5
|
||||
switchThumbChecked=#3f51b5
|
||||
chat_messagePanelSend=#3f51b5
|
||||
chat_messagePanelVoiceBackground=#3f51b5
|
||||
chat_messagePanelVoiceShadow=#3f51b5
|
||||
chat_outBubble=#3f51b5
|
||||
chat_recordVoiceCancel=#3f51b5
|
||||
chat_recordedVoiceBackground=#3f51b5
|
||||
chat_recordedVoiceDot=#3f51b5
|
||||
chat_replyPanelIcons=#3f51b5
|
||||
chat_replyPanelName=#3f51b5
|
||||
chat_searchPanelIcons=#3f51b5
|
||||
chat_secretTimerBackground=#3f51b5
|
||||
chat_topPanelLine=#3f51b5
|
||||
chat_topPanelTitle=#3f51b5
|
||||
chat_unreadMessagesStartArrowIcon=#3f51b5
|
||||
chat_unreadMessagesStartText=#3f51b5
|
||||
chat_addContact=#3f51b5
|
||||
chat_botKeyboardButtonBackground=#3f51b5
|
||||
chat_botSwitchToInlineText=#3f51b5
|
||||
chat_editDoneIcon=#3f51b5
|
||||
chat_emojiPanelIconSelected=#3f51b5
|
||||
chat_emojiPanelIconSelector=#3f51b5
|
||||
chat_emojiPanelMasksIconSelected=#3f51b5
|
||||
chat_emojiPanelNewTrending=#3f51b5
|
||||
chat_emojiPanelStickerPackSelector=#3f51b5
|
||||
chat_fieldOverlayText=#3f51b5
|
||||
chat_gifSaveHintBackground=#3f51b5
|
||||
chat_goDownButton=#3f51b5
|
||||
chat_goDownButtonCounter=#3f51b5
|
||||
chat_inlineResultIcon=#3f51b5
|
||||
chat_outAudioProgress=#3f51b5
|
||||
chat_outAudioSelectedProgress=#3f51b5
|
||||
chat_outContactIcon=#3f51b5
|
||||
chat_outFileIcon=#3f51b5
|
||||
chat_outFileProgress=#3f51b5
|
||||
chat_outFileProgressSelected=#3f51b5
|
||||
chat_outFileSelectedIcon=#3f51b5
|
||||
chat_outLoaderPhoto=#3f51b5
|
||||
chat_outLoaderPhotoSelected=#3f51b5
|
||||
chat_outLocationIcon=#3f51b5
|
||||
chat_messageLinkIn=#3f51b5
|
||||
chat_inAudioSeekbarFill=#3f51b5
|
||||
chat_inAudioTitleText=#3f51b5
|
||||
chat_inContactBackground=#3f51b5
|
||||
chat_inContactNameText=#3f51b5
|
||||
chat_inFileBackground=#3f51b5
|
||||
chat_inFileBackgroundSelected=#3f51b5
|
||||
chat_inFileNameText=#3f51b5
|
||||
chat_inInstant=#3f51b5
|
||||
chat_inLoader=#3f51b5
|
||||
chat_inLoaderPhotoIcon=#3f51b5
|
||||
chat_inLoaderPhotoIconSelected=#3f51b5
|
||||
chat_inLoaderSelected=#3f51b5
|
||||
chat_inLocationBackground=#3f51b5
|
||||
chat_inPreviewInstantText=#3f51b5
|
||||
chat_inPreviewLine=#3f51b5
|
||||
chat_inReplyLine=#3f51b5
|
||||
chat_inReplyNameText=#3f51b5
|
||||
chat_inSiteNameText=#3f51b5
|
||||
chat_inVenueNameText=#3f51b5
|
||||
chat_inVoiceSeekbarFill=#3f51b5
|
||||
avatar_backgroundSaved=#3f51b5
|
||||
contacts_inviteBackground=#3f51b5
|
||||
dialogTopBackground=#3f51b5
|
||||
location_liveLocationProgress=#3f51b5
|
||||
location_placeLocationBackground=#3f51b5
|
||||
chats_menuItemCheck=#3f51b5
|
||||
windowBackgroundWhiteLinkSelection=#403f51b5
|
||||
chat_selectedBackground=#403f51b5
|
||||
chat_inAudioSeekbar=#403f51b5
|
||||
chat_inAudioSeekbarSelected=#403f51b5
|
||||
chat_inVoiceSeekbar=#403f51b5
|
||||
chat_inVoiceSeekbarSelected=#403f51b5
|
||||
player_actionBarSubtitle=#803f51b5
|
||||
player_actionBarSelector=#803f51b5
|
||||
checkboxSquareDisabled=#803f51b5
|
||||
dialogCheckboxSquareDisabled=#803f51b5
|
||||
dialogLinkSelection=#803f51b5
|
||||
featuredStickers_addButtonPressed=#803f51b5
|
||||
profile_adminIcon=#803f51b5
|
||||
switchTrackChecked=#803f51b5
|
||||
chat_botKeyboardButtonBackgroundPressed=#803f51b5
|
||||
chat_linkSelectBackground=#803f51b5
|
||||
chat_inAudioDurationSelectedText=#803f51b5
|
||||
chat_inAudioDurationText=#803f51b5
|
||||
chat_inForwardedNameText=#803f51b5
|
||||
chat_inInstantSelected=#803f51b5
|
||||
chat_inMenu=#803f51b5
|
||||
chat_inMenuSelected=#803f51b5
|
||||
chat_inPreviewInstantSelectedText=#803f51b5
|
||||
chat_inSentClock=#803f51b5
|
||||
chat_inSentClockSelected=#803f51b5
|
||||
chat_inTimeSelectedText=#803f51b5
|
||||
chat_inTimeText=#803f51b5
|
||||
chat_inVenueInfoSelectedText=#803f51b5
|
||||
chat_inVenueInfoText=#803f51b5
|
||||
chat_inViaBotNameText=#803f51b5
|
||||
chat_inViews=#803f51b5
|
||||
chat_inViewsSelected=#803f51b5
|
||||
chat_adminText=#803f51b5
|
||||
chat_adminSelectedText=#803f51b5
|
||||
key_player_progressCachedBackground=#803f51b5
|
||||
chat_inAudioCacheSeekbar=#803f51b5
|
||||
chat_outAudioCacheSeekbar=#803f51b5
|
||||
chat_outBubbleSelected=#c03f51b5
|
||||
chat_inAudioPerfomerText=#c03f51b5
|
||||
chat_inContactPhoneText=#c03f51b5
|
||||
chat_inFileInfoSelectedText=#c03f51b5
|
||||
chat_inFileInfoText=#c03f51b5
|
||||
chat_inReplyMediaMessageSelectedText=#c03f51b5
|
||||
chat_inReplyMediaMessageText=#c03f51b5
|
||||
groupcreate_spanBackground=#7986cb
|
||||
chat_textSelectBackground=#7986cb
|
||||
chat_outBubbleShadow=#1a237e
|
Binary file not shown.
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"inbounds": [{
|
||||
"tag": "socks-in",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 114514,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"udp": true,
|
||||
"userLevel": 8
|
||||
},
|
||||
"sniffing": {
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
],
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [{
|
||||
"tag": "proxy",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "v2ray.cool",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"alterId": 64,
|
||||
"id": "73670f86-1145-4ffd-b468-6cd73cea1f29",
|
||||
"level": 8,
|
||||
"security": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "ws",
|
||||
"security": "tls",
|
||||
"tlssettings": {
|
||||
"allowInsecure": true,
|
||||
"serverName": "baidu.com"
|
||||
},
|
||||
"wssettings": {
|
||||
"connectionReuse": true,
|
||||
"headers": {
|
||||
"Host": "baidu.com"
|
||||
},
|
||||
"path": "/search"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [{
|
||||
"inboundTag": [
|
||||
"socks-in"
|
||||
],
|
||||
"outboundTag": "proxy",
|
||||
"type": "field"
|
||||
}]
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Enumerates barcode formats known to this package. Please keep alphabetized.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum BarcodeFormat {
|
||||
/** QR Code 2D barcode format. */
|
||||
QR_CODE
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
|
||||
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
|
||||
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
|
||||
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public abstract class Binarizer {
|
||||
|
||||
private final LuminanceSource source;
|
||||
|
||||
protected Binarizer(LuminanceSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public final LuminanceSource getLuminanceSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public abstract BitMatrix getBlackMatrix() throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param source The LuminanceSource this Binarizer will operate on.
|
||||
* @return A new concrete Binarizer implementation object.
|
||||
*/
|
||||
public abstract Binarizer createBinarizer(LuminanceSource source);
|
||||
|
||||
public final int getWidth() {
|
||||
return source.getWidth();
|
||||
}
|
||||
|
||||
public final int getHeight() {
|
||||
return source.getHeight();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
|
||||
* accept a BinaryBitmap and attempt to decode it.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class BinaryBitmap {
|
||||
|
||||
private final Binarizer binarizer;
|
||||
private BitMatrix matrix;
|
||||
|
||||
public BinaryBitmap(Binarizer binarizer) {
|
||||
if (binarizer == null) {
|
||||
throw new IllegalArgumentException("Binarizer must be non-null.");
|
||||
}
|
||||
this.binarizer = binarizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return binarizer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return binarizer.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
|
||||
return binarizer.getBlackRow(y, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if (matrix == null) {
|
||||
matrix = binarizer.getBlackMatrix();
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap can be cropped.
|
||||
*/
|
||||
public boolean isCropSupported() {
|
||||
return binarizer.getLuminanceSource().isCropSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public BinaryBitmap crop(int left, int top, int width, int height) {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||
*/
|
||||
public boolean isRotateSupported() {
|
||||
return binarizer.getLuminanceSource().isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise45() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getBlackMatrix().toString();
|
||||
} catch (NotFoundException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected and decoded, but
|
||||
* was not returned because its checksum feature failed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class ChecksumException extends ReaderException {
|
||||
|
||||
private static final ChecksumException INSTANCE = new ChecksumException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private ChecksumException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private ChecksumException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance() {
|
||||
return isStackTrace ? new ChecksumException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance(Throwable cause) {
|
||||
return isStackTrace ? new ChecksumException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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 Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @see Reader#decode(BinaryBitmap,java.util.Map)
|
||||
*/
|
||||
public enum DecodeHintType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
OTHER(Object.class),
|
||||
|
||||
/**
|
||||
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
PURE_BARCODE(Void.class),
|
||||
|
||||
/**
|
||||
* Image is known to be of one of a few possible formats.
|
||||
* Maps to a {@link List} of {@link BarcodeFormat}s.
|
||||
*/
|
||||
POSSIBLE_FORMATS(List.class),
|
||||
|
||||
/**
|
||||
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
|
||||
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
TRY_HARDER(Void.class),
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||
*/
|
||||
CHARACTER_SET(String.class),
|
||||
|
||||
/**
|
||||
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
|
||||
*/
|
||||
ALLOWED_LENGTHS(int[].class),
|
||||
|
||||
/**
|
||||
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_CODE_39_CHECK_DIGIT(Void.class),
|
||||
|
||||
/**
|
||||
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
|
||||
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_GS1(Void.class),
|
||||
|
||||
/**
|
||||
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
|
||||
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
|
||||
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
RETURN_CODABAR_START_END(Void.class),
|
||||
|
||||
/**
|
||||
* The caller needs to be notified via callback when a possible {@link ResultPoint}
|
||||
* is found. Maps to a {@link ResultPointCallback}.
|
||||
*/
|
||||
NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),
|
||||
|
||||
|
||||
/**
|
||||
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
|
||||
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
|
||||
* If it is optional to have an extension, do not set this hint. If this is set,
|
||||
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
|
||||
* at all.
|
||||
*/
|
||||
ALLOWED_EAN_EXTENSIONS(int[].class),
|
||||
|
||||
// End of enumeration values.
|
||||
;
|
||||
|
||||
/**
|
||||
* Data type the hint is expecting.
|
||||
* Among the possible values the {@link Void} stands out as being used for
|
||||
* hints that do not expect a value to be supplied (flag hints). Such hints
|
||||
* will possibly have their value ignored, or replaced by a
|
||||
* {@link Boolean#TRUE}. Hint suppliers should probably use
|
||||
* {@link Boolean#TRUE} as directed by the actual hint documentation.
|
||||
*/
|
||||
private final Class<?> valueType;
|
||||
|
||||
DecodeHintType(Class<?> valueType) {
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public Class<?> getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Simply encapsulates a width and height.
|
||||
*/
|
||||
public final class Dimension {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public Dimension(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Dimension) {
|
||||
Dimension d = (Dimension) other;
|
||||
return width == d.width && height == d.height;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return width * 32713 + height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return width + "x" + height;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* These are a set of hints that you may pass to Writers to specify their behavior.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public enum EncodeHintType {
|
||||
|
||||
/**
|
||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||
* Type depends on the encoder. For example for QR codes it's type
|
||||
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
|
||||
* In all cases, it can also be a {@link String} representation of the desired value as well.
|
||||
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||
*/
|
||||
ERROR_CORRECTION,
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use where applicable (type {@link String})
|
||||
*/
|
||||
CHARACTER_SET,
|
||||
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
DATA_MATRIX_SHAPE,
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated use width/height params in
|
||||
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
MIN_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated without replacement
|
||||
*/
|
||||
@Deprecated
|
||||
MAX_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||
* by format; for example it controls margin before and after the barcode horizontally for
|
||||
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
MARGIN,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String} value).
|
||||
*/
|
||||
PDF417_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies what compaction mode to use for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
|
||||
* enum values).
|
||||
*/
|
||||
PDF417_COMPACTION,
|
||||
|
||||
/**
|
||||
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||
*/
|
||||
PDF417_DIMENSIONS,
|
||||
|
||||
/**
|
||||
* Specifies the required number of layers for an Aztec code.
|
||||
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
|
||||
* 0 indicates to use the minimum number of layers (the default).
|
||||
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
AZTEC_LAYERS,
|
||||
|
||||
/**
|
||||
* Specifies the exact version of QR code to be encoded.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
QR_VERSION,
|
||||
|
||||
/**
|
||||
* Specifies the QR code mask pattern to be used. Allowed values are
|
||||
* 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
|
||||
* the optimal mask pattern.
|
||||
* * (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
QR_MASK_PATTERN,
|
||||
|
||||
/**
|
||||
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
*/
|
||||
GS1_FORMAT,
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected, but some aspect of
|
||||
* the content did not conform to the barcode's format rules. This could have
|
||||
* been due to a mis-detection.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class FormatException extends ReaderException {
|
||||
|
||||
private static final FormatException INSTANCE = new FormatException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private FormatException() {
|
||||
}
|
||||
|
||||
private FormatException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance() {
|
||||
return isStackTrace ? new FormatException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance(Throwable cause) {
|
||||
return isStackTrace ? new FormatException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class InvertedLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final LuminanceSource delegate;
|
||||
|
||||
public InvertedLuminanceSource(LuminanceSource delegate) {
|
||||
super(delegate.getWidth(), delegate.getHeight());
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
row = delegate.getRow(y, row);
|
||||
int width = getWidth();
|
||||
for (int i = 0; i < width; i++) {
|
||||
row[i] = (byte) (255 - (row[i] & 0xFF));
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
byte[] matrix = delegate.getMatrix();
|
||||
int length = getWidth() * getHeight();
|
||||
byte[] invertedMatrix = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
invertedMatrix[i] = (byte) (255 - (matrix[i] & 0xFF));
|
||||
}
|
||||
return invertedMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return delegate.isCropSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new InvertedLuminanceSource(delegate.crop(left, top, width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotateSupported() {
|
||||
return delegate.isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return original delegate {@link LuminanceSource} since invert undoes itself
|
||||
*/
|
||||
@Override
|
||||
public LuminanceSource invert() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise45());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
||||
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
||||
* only provides immutable methods; therefore crop and rotation create copies. This is to ensure
|
||||
* that one Reader does not modify the original luminance source and leave it in an unknown state
|
||||
* for other Readers in the chain.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public abstract class LuminanceSource {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
protected LuminanceSource(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
|
||||
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
|
||||
* to bitwise and with 0xff for each value. It is preferable for implementations of this method
|
||||
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
|
||||
* getMatrix() may never be called.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0,getHeight())
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* Always use the returned object, and ignore the .length of the array.
|
||||
* @return An array containing the luminance data.
|
||||
*/
|
||||
public abstract byte[] getRow(int y, byte[] row);
|
||||
|
||||
/**
|
||||
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
|
||||
* {@code int luminance = array[y * width + x] & 0xff}
|
||||
*
|
||||
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
|
||||
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||
* of the result.
|
||||
*/
|
||||
public abstract byte[] getMatrix();
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public final int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public final int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports cropping.
|
||||
*/
|
||||
public boolean isCropSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
throw new UnsupportedOperationException("This luminance source does not support cropping.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
public boolean isRotateSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
public LuminanceSource invert() {
|
||||
return new InvertedLuminanceSource(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 90 degrees.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 45 degrees.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
byte[] row = new byte[width];
|
||||
StringBuilder result = new StringBuilder(height * (width + 1));
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = getRow(y, row);
|
||||
for (int x = 0; x < width; x++) {
|
||||
int luminance = row[x] & 0xFF;
|
||||
char c;
|
||||
if (luminance < 0x40) {
|
||||
c = '#';
|
||||
} else if (luminance < 0x80) {
|
||||
c = '+';
|
||||
} else if (luminance < 0xC0) {
|
||||
c = '.';
|
||||
} else {
|
||||
c = ' ';
|
||||
}
|
||||
result.append(c);
|
||||
}
|
||||
result.append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was not found in the image. It might have been
|
||||
* partially detected but could not be confirmed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class NotFoundException extends ReaderException {
|
||||
|
||||
private static final NotFoundException INSTANCE = new NotFoundException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private NotFoundException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public static NotFoundException getNotFoundInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* This object extends LuminanceSource around an array of YUV data returned from the camera driver,
|
||||
* with the option to crop to a rectangle within the full data. This can be used to exclude
|
||||
* superfluous pixels around the perimeter and speed up decoding.
|
||||
*
|
||||
* It works for any pixel format where the Y channel is planar and appears first, including
|
||||
* YCbCr_420_SP and YCbCr_422_SP.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class PlanarYUVLuminanceSource extends LuminanceSource {
|
||||
|
||||
private static final int THUMBNAIL_SCALE_FACTOR = 2;
|
||||
|
||||
private final byte[] yuvData;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public PlanarYUVLuminanceSource(byte[] yuvData,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height,
|
||||
boolean reverseHorizontal) {
|
||||
super(width, height);
|
||||
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
this.yuvData = yuvData;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
if (reverseHorizontal) {
|
||||
reverseHorizontal(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(yuvData, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return yuvData;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(yuvData, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new PlanarYUVLuminanceSource(yuvData,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height,
|
||||
false);
|
||||
}
|
||||
|
||||
public int[] renderThumbnail() {
|
||||
int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
int[] pixels = new int[width * height];
|
||||
byte[] yuv = yuvData;
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
|
||||
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
|
||||
}
|
||||
inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as RGB data from
|
||||
* an ARGB pixel array. It does not support rotation.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Betaminos
|
||||
*/
|
||||
public final class RGBLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final byte[] luminances;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public RGBLuminanceSource(int width, int height, int[] pixels) {
|
||||
super(width, height);
|
||||
|
||||
dataWidth = width;
|
||||
dataHeight = height;
|
||||
left = 0;
|
||||
top = 0;
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
//
|
||||
// Total number of pixels suffices, can ignore shape
|
||||
int size = width * height;
|
||||
luminances = new byte[size];
|
||||
for (int offset = 0; offset < size; offset++) {
|
||||
int pixel = pixels[offset];
|
||||
int r = (pixel >> 16) & 0xff; // red
|
||||
int g2 = (pixel >> 7) & 0x1fe; // 2 * green
|
||||
int b = pixel & 0xff; // blue
|
||||
// Calculate green-favouring average cheaply
|
||||
luminances[offset] = (byte) ((r + g2 + b) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
private RGBLuminanceSource(byte[] pixels,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height) {
|
||||
super(width, height);
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
this.luminances = pixels;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(luminances, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return luminances;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(luminances, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new RGBLuminanceSource(luminances,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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 MultiFormatReader}, which attempts to determine what barcode
|
||||
* format is present within the image as well, and then decodes it accordingly.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author 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 NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;
|
||||
|
||||
/**
|
||||
* 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 Map} from {@link DecodeHintType}
|
||||
* to arbitrary data. The
|
||||
* meaning of the data depends upon the hint type. The implementation may or may not do
|
||||
* anything with these hints.
|
||||
* @return String which the barcode encodes
|
||||
* @throws NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, ChecksumException, FormatException;
|
||||
|
||||
/**
|
||||
* Resets any internal state the implementation has after a decode, to prepare it
|
||||
* for reuse.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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 Sean Owen
|
||||
*/
|
||||
public abstract class ReaderException extends Exception {
|
||||
|
||||
// disable stack traces when not running inside test units
|
||||
protected static final boolean isStackTrace =
|
||||
System.getProperty("surefire.test.class.path") != null;
|
||||
protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0];
|
||||
|
||||
ReaderException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ReaderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
// Prevent stack traces from being taken
|
||||
@Override
|
||||
public final synchronized Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of decoding a barcode within an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class Result {
|
||||
|
||||
private final String text;
|
||||
private final byte[] rawBytes;
|
||||
private final int numBits;
|
||||
private ResultPoint[] resultPoints;
|
||||
private final BarcodeFormat format;
|
||||
private Map<ResultMetadataType,Object> resultMetadata;
|
||||
private final long timestamp;
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format) {
|
||||
this(text, rawBytes, resultPoints, format, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format,
|
||||
long timestamp) {
|
||||
this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length,
|
||||
resultPoints, format, timestamp);
|
||||
}
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
int numBits,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format,
|
||||
long timestamp) {
|
||||
this.text = text;
|
||||
this.rawBytes = rawBytes;
|
||||
this.numBits = numBits;
|
||||
this.resultPoints = resultPoints;
|
||||
this.format = format;
|
||||
this.resultMetadata = null;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw text encoded by the barcode
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public int getNumBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||
*/
|
||||
public BarcodeFormat getBarcodeFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public Map<ResultMetadataType,Object> getResultMetadata() {
|
||||
return resultMetadata;
|
||||
}
|
||||
|
||||
public void putMetadata(ResultMetadataType type, Object value) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = new EnumMap<>(ResultMetadataType.class);
|
||||
}
|
||||
resultMetadata.put(type, value);
|
||||
}
|
||||
|
||||
public void putAllMetadata(Map<ResultMetadataType,Object> metadata) {
|
||||
if (metadata != null) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = metadata;
|
||||
} else {
|
||||
resultMetadata.putAll(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addResultPoints(ResultPoint[] newPoints) {
|
||||
ResultPoint[] oldPoints = resultPoints;
|
||||
if (oldPoints == null) {
|
||||
resultPoints = newPoints;
|
||||
} else if (newPoints != null && newPoints.length > 0) {
|
||||
ResultPoint[] allPoints = new ResultPoint[oldPoints.length + newPoints.length];
|
||||
System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length);
|
||||
System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length);
|
||||
resultPoints = allPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Represents some type of metadata about the result of the decoding that the decoder
|
||||
* wishes to communicate back to the caller.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum ResultMetadataType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
OTHER,
|
||||
|
||||
/**
|
||||
* Denotes the likely approximate orientation of the barcode in the image. This value
|
||||
* is given as degrees rotated clockwise from the normal, upright orientation.
|
||||
* For example a 1D barcode which was found by reading top-to-bottom would be
|
||||
* said to have orientation "90". This key maps to an {@link Integer} whose
|
||||
* value is in the range [0,360).
|
||||
*/
|
||||
ORIENTATION,
|
||||
|
||||
/**
|
||||
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
|
||||
* which is sometimes used to encode binary data. While {@link Result} makes available
|
||||
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
|
||||
* from the byte segments alone.</p>
|
||||
*
|
||||
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
||||
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||
*/
|
||||
BYTE_SEGMENTS,
|
||||
|
||||
/**
|
||||
* Error correction level used, if applicable. The value type depends on the
|
||||
* format, but is typically a String.
|
||||
*/
|
||||
ERROR_CORRECTION_LEVEL,
|
||||
|
||||
/**
|
||||
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||
*/
|
||||
ISSUE_NUMBER,
|
||||
|
||||
/**
|
||||
* For some products, indicates the suggested retail price in the barcode as a
|
||||
* formatted {@link String}.
|
||||
*/
|
||||
SUGGESTED_PRICE,
|
||||
|
||||
/**
|
||||
* For some products, the possible country of manufacture as a {@link String} denoting the
|
||||
* ISO country code. Some map to multiple possible countries, like "US/CA".
|
||||
*/
|
||||
POSSIBLE_COUNTRY,
|
||||
|
||||
/**
|
||||
* For some products, the extension text
|
||||
*/
|
||||
UPC_EAN_EXTENSION,
|
||||
|
||||
/**
|
||||
* PDF417-specific metadata
|
||||
*/
|
||||
PDF417_EXTRA_METADATA,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* sequence number is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_SEQUENCE,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* parity is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_PARITY,
|
||||
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.detector.MathUtils;
|
||||
|
||||
/**
|
||||
* <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 Sean Owen
|
||||
*/
|
||||
public class ResultPoint {
|
||||
|
||||
private final float x;
|
||||
private final float y;
|
||||
|
||||
public ResultPoint(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public final float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public final float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object other) {
|
||||
if (other instanceof ResultPoint) {
|
||||
ResultPoint otherPoint = (ResultPoint) other;
|
||||
return x == otherPoint.x && y == otherPoint.y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "(" + x + ',' + y + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
||||
* and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||
*
|
||||
* @param patterns array of three {@code ResultPoint} to order
|
||||
*/
|
||||
public static void orderBestPatterns(ResultPoint[] patterns) {
|
||||
|
||||
// Find distances between pattern centers
|
||||
float zeroOneDistance = distance(patterns[0], patterns[1]);
|
||||
float oneTwoDistance = distance(patterns[1], patterns[2]);
|
||||
float zeroTwoDistance = distance(patterns[0], patterns[2]);
|
||||
|
||||
ResultPoint pointA;
|
||||
ResultPoint pointB;
|
||||
ResultPoint pointC;
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
|
||||
pointB = patterns[0];
|
||||
pointA = patterns[1];
|
||||
pointC = patterns[2];
|
||||
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
|
||||
pointB = patterns[1];
|
||||
pointA = patterns[0];
|
||||
pointC = patterns[2];
|
||||
} else {
|
||||
pointB = patterns[2];
|
||||
pointA = patterns[0];
|
||||
pointC = patterns[1];
|
||||
}
|
||||
|
||||
// Use cross product to figure out whether A and C are correct or flipped.
|
||||
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
||||
// should swap A and C.
|
||||
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
|
||||
ResultPoint temp = pointA;
|
||||
pointA = pointC;
|
||||
pointC = temp;
|
||||
}
|
||||
|
||||
patterns[0] = pointA;
|
||||
patterns[1] = pointB;
|
||||
patterns[2] = pointC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pattern1 first pattern
|
||||
* @param pattern2 second pattern
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
|
||||
return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the z component of the cross product between vectors BC and BA.
|
||||
*/
|
||||
private static float crossProductZ(ResultPoint pointA,
|
||||
ResultPoint pointB,
|
||||
ResultPoint pointC) {
|
||||
float bX = pointB.x;
|
||||
float bY = pointB.y;
|
||||
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Callback which is invoked when a possible result point (significant
|
||||
* point in the barcode image such as a corner) is found.
|
||||
*
|
||||
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
|
||||
*/
|
||||
public interface ResultPointCallback {
|
||||
|
||||
void foundPossibleResultPoint(ResultPoint point);
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* A base class which covers the range of exceptions which may occur when encoding a barcode using
|
||||
* the Writer framework.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class WriterException extends Exception {
|
||||
|
||||
public WriterException() {
|
||||
}
|
||||
|
||||
public WriterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WriterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,357 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class BitArray implements Cloneable {
|
||||
|
||||
private int[] bits;
|
||||
private int size;
|
||||
|
||||
public BitArray() {
|
||||
this.size = 0;
|
||||
this.bits = new int[1];
|
||||
}
|
||||
|
||||
public BitArray(int size) {
|
||||
this.size = size;
|
||||
this.bits = makeArray(size);
|
||||
}
|
||||
|
||||
// For testing only
|
||||
BitArray(int[] bits, int size) {
|
||||
this.bits = bits;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getSizeInBytes() {
|
||||
return (size + 7) / 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int size) {
|
||||
if (size > bits.length * 32) {
|
||||
int[] newBits = makeArray(size);
|
||||
System.arraycopy(bits, 0, newBits, 0, bits.length);
|
||||
this.bits = newBits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i bit to get
|
||||
* @return true iff bit i is set
|
||||
*/
|
||||
public boolean get(int i) {
|
||||
return (bits[i / 32] & (1 << (i & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void set(int i) {
|
||||
bits[i / 32] |= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void flip(int i) {
|
||||
bits[i / 32] ^= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from first bit to check
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public int getNextSet(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= -(1 << (from & 0x1F));
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return Math.min(result, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from index to start looking for unset bit
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public int getNextUnset(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = ~bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= -(1 << (from & 0x1F));
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = ~bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return Math.min(result, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block of 32 bits, starting at bit i.
|
||||
*
|
||||
* @param i first bit to set
|
||||
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
|
||||
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||
*/
|
||||
public void setBulk(int i, int newBits) {
|
||||
bits[i / 32] = newBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
*/
|
||||
public void setRange(int start, int end) {
|
||||
if (end < start || start < 0 || end > size) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (end == start) {
|
||||
return;
|
||||
}
|
||||
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
int firstInt = start / 32;
|
||||
int lastInt = end / 32;
|
||||
for (int i = firstInt; i <= lastInt; i++) {
|
||||
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
||||
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
||||
// Ones from firstBit to lastBit, inclusive
|
||||
int mask = (2 << lastBit) - (1 << firstBit);
|
||||
bits[i] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public void clear() {
|
||||
int max = bits.length;
|
||||
for (int i = 0; i < max; i++) {
|
||||
bits[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficient method to check if a range of bits is set, or not set.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
|
||||
* @return true iff all bits are set or not set in range, according to value argument
|
||||
* @throws IllegalArgumentException if end is less than start or the range is not contained in the array
|
||||
*/
|
||||
public boolean isRange(int start, int end, boolean value) {
|
||||
if (end < start || start < 0 || end > size) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (end == start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
int firstInt = start / 32;
|
||||
int lastInt = end / 32;
|
||||
for (int i = firstInt; i <= lastInt; i++) {
|
||||
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
||||
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
||||
// Ones from firstBit to lastBit, inclusive
|
||||
int mask = (2 << lastBit) - (1 << firstBit);
|
||||
|
||||
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||
if ((bits[i] & mask) != (value ? mask : 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void appendBit(boolean bit) {
|
||||
ensureCapacity(size + 1);
|
||||
if (bit) {
|
||||
bits[size / 32] |= 1 << (size & 0x1F);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the least-significant bits, from value, in order from most-significant to
|
||||
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||
* 0, 1, 1, 1, 1, 0 in that order.
|
||||
*
|
||||
* @param value {@code int} containing bits to append
|
||||
* @param numBits bits from value to append
|
||||
*/
|
||||
public void appendBits(int value, int numBits) {
|
||||
if (numBits < 0 || numBits > 32) {
|
||||
throw new IllegalArgumentException("Num bits must be between 0 and 32");
|
||||
}
|
||||
ensureCapacity(size + numBits);
|
||||
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
|
||||
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendBitArray(BitArray other) {
|
||||
int otherSize = other.size;
|
||||
ensureCapacity(size + otherSize);
|
||||
for (int i = 0; i < otherSize; i++) {
|
||||
appendBit(other.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void xor(BitArray other) {
|
||||
if (size != other.size) {
|
||||
throw new IllegalArgumentException("Sizes don't match");
|
||||
}
|
||||
for (int i = 0; i < bits.length; i++) {
|
||||
// The last int could be incomplete (i.e. not have 32 bits in
|
||||
// it) but there is no problem since 0 XOR 0 == 0.
|
||||
bits[i] ^= other.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bitOffset first bit to start writing
|
||||
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||
* @param offset position in array to start writing
|
||||
* @param numBytes how many bytes to write
|
||||
*/
|
||||
public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
int theByte = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (get(bitOffset)) {
|
||||
theByte |= 1 << (7 - j);
|
||||
}
|
||||
bitOffset++;
|
||||
}
|
||||
array[offset + i] = (byte) theByte;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses all bits in the array.
|
||||
*/
|
||||
public void reverse() {
|
||||
int[] newBits = new int[bits.length];
|
||||
// reverse all int's first
|
||||
int len = (size - 1) / 32;
|
||||
int oldBitsLen = len + 1;
|
||||
for (int i = 0; i < oldBitsLen; i++) {
|
||||
long x = bits[i];
|
||||
x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1);
|
||||
x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2);
|
||||
x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4);
|
||||
x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8);
|
||||
x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16);
|
||||
newBits[len - i] = (int) x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if (size != oldBitsLen * 32) {
|
||||
int leftOffset = oldBitsLen * 32 - size;
|
||||
int currentInt = newBits[0] >>> leftOffset;
|
||||
for (int i = 1; i < oldBitsLen; i++) {
|
||||
int nextInt = newBits[i];
|
||||
currentInt |= nextInt << (32 - leftOffset);
|
||||
newBits[i - 1] = currentInt;
|
||||
currentInt = nextInt >>> leftOffset;
|
||||
}
|
||||
newBits[oldBitsLen - 1] = currentInt;
|
||||
}
|
||||
bits = newBits;
|
||||
}
|
||||
|
||||
private static int[] makeArray(int size) {
|
||||
return new int[(size + 31) / 32];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
BitArray other = (BitArray) o;
|
||||
return size == other.size && Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * size + Arrays.hashCode(bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(size + (size / 8) + 1);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
result.append(' ');
|
||||
}
|
||||
result.append(get(i) ? 'X' : '.');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitArray clone() {
|
||||
return new BitArray(bits.clone(), size);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,488 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
|
||||
* module, x is the column position, and y is the row position. The ordering is always x, y.
|
||||
* The origin is at the top-left.</p>
|
||||
*
|
||||
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
|
||||
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
|
||||
* efficiently.</p>
|
||||
*
|
||||
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
|
||||
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class BitMatrix implements Cloneable {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int rowSize;
|
||||
private final int[] bits;
|
||||
private final int multiple;
|
||||
|
||||
/**
|
||||
* Creates an empty square {@code BitMatrix}.
|
||||
*
|
||||
* @param dimension height and width
|
||||
*/
|
||||
public BitMatrix(int dimension) {
|
||||
this(dimension, dimension, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty {@code BitMatrix}.
|
||||
*
|
||||
* @param width bit matrix width
|
||||
* @param height bit matrix height
|
||||
*/
|
||||
public BitMatrix(int width, int height, int multiple) {
|
||||
if (width < 1 || height < 1) {
|
||||
throw new IllegalArgumentException("Both dimensions must be greater than 0");
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.multiple = multiple;
|
||||
this.rowSize = (width + 31) / 32;
|
||||
bits = new int[rowSize * height];
|
||||
}
|
||||
|
||||
private BitMatrix(int width, int height, int rowSize, int[] bits) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rowSize = rowSize;
|
||||
this.bits = bits;
|
||||
this.multiple = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interprets a 2D array of booleans as a {@code BitMatrix}, where "true" means an "on" bit.
|
||||
*
|
||||
* @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows
|
||||
* @return {@code BitMatrix} representation of image
|
||||
*/
|
||||
public static BitMatrix parse(boolean[][] image) {
|
||||
int height = image.length;
|
||||
int width = image[0].length;
|
||||
BitMatrix bits = new BitMatrix(width, height, 1);
|
||||
for (int i = 0; i < height; i++) {
|
||||
boolean[] imageI = image[i];
|
||||
for (int j = 0; j < width; j++) {
|
||||
if (imageI[j]) {
|
||||
bits.set(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) {
|
||||
if (stringRepresentation == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
boolean[] bits = new boolean[stringRepresentation.length()];
|
||||
int bitsPos = 0;
|
||||
int rowStartPos = 0;
|
||||
int rowLength = -1;
|
||||
int nRows = 0;
|
||||
int pos = 0;
|
||||
while (pos < stringRepresentation.length()) {
|
||||
if (stringRepresentation.charAt(pos) == '\n' ||
|
||||
stringRepresentation.charAt(pos) == '\r') {
|
||||
if (bitsPos > rowStartPos) {
|
||||
if (rowLength == -1) {
|
||||
rowLength = bitsPos - rowStartPos;
|
||||
} else if (bitsPos - rowStartPos != rowLength) {
|
||||
throw new IllegalArgumentException("row lengths do not match");
|
||||
}
|
||||
rowStartPos = bitsPos;
|
||||
nRows++;
|
||||
}
|
||||
pos++;
|
||||
} else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) {
|
||||
pos += setString.length();
|
||||
bits[bitsPos] = true;
|
||||
bitsPos++;
|
||||
} else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) {
|
||||
pos += unsetString.length();
|
||||
bits[bitsPos] = false;
|
||||
bitsPos++;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"illegal character encountered: " + stringRepresentation.substring(pos));
|
||||
}
|
||||
}
|
||||
|
||||
// no EOL at end?
|
||||
if (bitsPos > rowStartPos) {
|
||||
if (rowLength == -1) {
|
||||
rowLength = bitsPos - rowStartPos;
|
||||
} else if (bitsPos - rowStartPos != rowLength) {
|
||||
throw new IllegalArgumentException("row lengths do not match");
|
||||
}
|
||||
nRows++;
|
||||
}
|
||||
|
||||
BitMatrix matrix = new BitMatrix(rowLength, nRows, 1);
|
||||
for (int i = 0; i < bitsPos; i++) {
|
||||
if (bits[i]) {
|
||||
matrix.set(i % rowLength, i / rowLength);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the requested bit, where true means black.</p>
|
||||
*
|
||||
* @param x The horizontal component (i.e. which column)
|
||||
* @param y The vertical component (i.e. which row)
|
||||
* @return value of given bit in matrix
|
||||
*/
|
||||
public boolean get(int x, int y) {
|
||||
int offset = y * rowSize + (x / 32);
|
||||
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the given bit to true.</p>
|
||||
*
|
||||
* @param x The horizontal component (i.e. which column)
|
||||
* @param y The vertical component (i.e. which row)
|
||||
*/
|
||||
public void set(int x, int y) {
|
||||
int offset = y * rowSize + (x / 32);
|
||||
bits[offset] |= 1 << (x & 0x1f);
|
||||
}
|
||||
|
||||
public void unset(int x, int y) {
|
||||
int offset = y * rowSize + (x / 32);
|
||||
bits[offset] &= ~(1 << (x & 0x1f));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Flips the given bit.</p>
|
||||
*
|
||||
* @param x The horizontal component (i.e. which column)
|
||||
* @param y The vertical component (i.e. which row)
|
||||
*/
|
||||
public void flip(int x, int y) {
|
||||
int offset = y * rowSize + (x / 32);
|
||||
bits[offset] ^= 1 << (x & 0x1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding
|
||||
* mask bit is set.
|
||||
*
|
||||
* @param mask XOR mask
|
||||
*/
|
||||
public void xor(BitMatrix mask) {
|
||||
if (width != mask.getWidth() || height != mask.getHeight()
|
||||
|| rowSize != mask.getRowSize()) {
|
||||
throw new IllegalArgumentException("input matrix dimensions do not match");
|
||||
}
|
||||
BitArray rowArray = new BitArray(width);
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * rowSize;
|
||||
int[] row = mask.getRow(y, rowArray).getBitArray();
|
||||
for (int x = 0; x < rowSize; x++) {
|
||||
bits[offset + x] ^= row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public void clear() {
|
||||
int max = bits.length;
|
||||
for (int i = 0; i < max; i++) {
|
||||
bits[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a square region of the bit matrix to true.</p>
|
||||
*
|
||||
* @param left The horizontal position to begin at (inclusive)
|
||||
* @param top The vertical position to begin at (inclusive)
|
||||
* @param width The width of the region
|
||||
* @param height The height of the region
|
||||
*/
|
||||
public void setRegion(int left, int top, int width, int height) {
|
||||
if (top < 0 || left < 0) {
|
||||
throw new IllegalArgumentException("Left and top must be nonnegative");
|
||||
}
|
||||
if (height < 1 || width < 1) {
|
||||
throw new IllegalArgumentException("Height and width must be at least 1");
|
||||
}
|
||||
int right = left + width;
|
||||
int bottom = top + height;
|
||||
if (bottom > this.height || right > this.width) {
|
||||
throw new IllegalArgumentException("The region must fit inside the matrix");
|
||||
}
|
||||
for (int y = top; y < bottom; y++) {
|
||||
int offset = y * rowSize;
|
||||
for (int x = left; x < right; x++) {
|
||||
bits[offset + (x / 32)] |= 1 << (x & 0x1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fast method to retrieve one row of data from the matrix as a BitArray.
|
||||
*
|
||||
* @param y The row to retrieve
|
||||
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
|
||||
* @return The resulting BitArray - this reference should always be used even when passing
|
||||
* your own row
|
||||
*/
|
||||
public BitArray getRow(int y, BitArray row) {
|
||||
if (row == null || row.getSize() < width) {
|
||||
row = new BitArray(width);
|
||||
} else {
|
||||
row.clear();
|
||||
}
|
||||
int offset = y * rowSize;
|
||||
for (int x = 0; x < rowSize; x++) {
|
||||
row.setBulk(x * 32, bits[offset + x]);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param y row to set
|
||||
* @param row {@link BitArray} to copy from
|
||||
*/
|
||||
public void setRow(int y, BitArray row) {
|
||||
System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
|
||||
*/
|
||||
public void rotate180() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
BitArray topRow = new BitArray(width);
|
||||
BitArray bottomRow = new BitArray(width);
|
||||
for (int i = 0; i < (height + 1) / 2; i++) {
|
||||
topRow = getRow(i, topRow);
|
||||
bottomRow = getRow(height - 1 - i, bottomRow);
|
||||
topRow.reverse();
|
||||
bottomRow.reverse();
|
||||
setRow(i, bottomRow);
|
||||
setRow(height - 1 - i, topRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
|
||||
*/
|
||||
public int[] getEnclosingRectangle() {
|
||||
int left = width;
|
||||
int top = height;
|
||||
int right = -1;
|
||||
int bottom = -1;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x32 = 0; x32 < rowSize; x32++) {
|
||||
int theBits = bits[y * rowSize + x32];
|
||||
if (theBits != 0) {
|
||||
if (y < top) {
|
||||
top = y;
|
||||
}
|
||||
if (y > bottom) {
|
||||
bottom = y;
|
||||
}
|
||||
if (x32 * 32 < left) {
|
||||
int bit = 0;
|
||||
while ((theBits << (31 - bit)) == 0) {
|
||||
bit++;
|
||||
}
|
||||
if ((x32 * 32 + bit) < left) {
|
||||
left = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
if (x32 * 32 + 31 > right) {
|
||||
int bit = 31;
|
||||
while ((theBits >>> bit) == 0) {
|
||||
bit--;
|
||||
}
|
||||
if ((x32 * 32 + bit) > right) {
|
||||
right = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (right < left || bottom < top) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new int[] {left, top, right - left + 1, bottom - top + 1};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting a corner of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white
|
||||
*/
|
||||
public int[] getTopLeftOnBit() {
|
||||
int bitsOffset = 0;
|
||||
while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
|
||||
bitsOffset++;
|
||||
}
|
||||
if (bitsOffset == bits.length) {
|
||||
return null;
|
||||
}
|
||||
int y = bitsOffset / rowSize;
|
||||
int x = (bitsOffset % rowSize) * 32;
|
||||
|
||||
int theBits = bits[bitsOffset];
|
||||
int bit = 0;
|
||||
while ((theBits << (31 - bit)) == 0) {
|
||||
bit++;
|
||||
}
|
||||
x += bit;
|
||||
return new int[] {x, y};
|
||||
}
|
||||
|
||||
public int[] getBottomRightOnBit() {
|
||||
int bitsOffset = bits.length - 1;
|
||||
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
|
||||
bitsOffset--;
|
||||
}
|
||||
if (bitsOffset < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int y = bitsOffset / rowSize;
|
||||
int x = (bitsOffset % rowSize) * 32;
|
||||
|
||||
int theBits = bits[bitsOffset];
|
||||
int bit = 31;
|
||||
while ((theBits >>> bit) == 0) {
|
||||
bit--;
|
||||
}
|
||||
x += bit;
|
||||
|
||||
return new int[] {x, y};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the matrix
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the matrix
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getMultiple() {
|
||||
return multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The row size of the matrix
|
||||
*/
|
||||
public int getRowSize() {
|
||||
return rowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitMatrix)) {
|
||||
return false;
|
||||
}
|
||||
BitMatrix other = (BitMatrix) o;
|
||||
return width == other.width && height == other.height && rowSize == other.rowSize &&
|
||||
Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = width;
|
||||
hash = 31 * hash + width;
|
||||
hash = 31 * hash + height;
|
||||
hash = 31 * hash + rowSize;
|
||||
hash = 31 * hash + Arrays.hashCode(bits);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string representation using "X" for set and " " for unset bits
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString("X ", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param setString representation of a set bit
|
||||
* @param unsetString representation of an unset bit
|
||||
* @return string representation of entire matrix utilizing given strings
|
||||
*/
|
||||
public String toString(String setString, String unsetString) {
|
||||
return buildToString(setString, unsetString, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param setString representation of a set bit
|
||||
* @param unsetString representation of an unset bit
|
||||
* @param lineSeparator newline character in string representation
|
||||
* @return string representation of entire matrix utilizing given strings and line separator
|
||||
* @deprecated call {@link #toString(String,String)} only, which uses \n line separator always
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String setString, String unsetString, String lineSeparator) {
|
||||
return buildToString(setString, unsetString, lineSeparator);
|
||||
}
|
||||
|
||||
private String buildToString(String setString, String unsetString, String lineSeparator) {
|
||||
StringBuilder result = new StringBuilder(height * (width + 1));
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
result.append(get(x, y) ? setString : unsetString);
|
||||
}
|
||||
result.append(lineSeparator);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix clone() {
|
||||
return new BitMatrix(width, height, rowSize, bits.clone());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
|
||||
* it passed in, in which case all bets are off.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class BitSource {
|
||||
|
||||
private final byte[] bytes;
|
||||
private int byteOffset;
|
||||
private int bitOffset;
|
||||
|
||||
/**
|
||||
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
|
||||
* Bits are read within a byte from most-significant to least-significant bit.
|
||||
*/
|
||||
public BitSource(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public int getBitOffset() {
|
||||
return bitOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public int getByteOffset() {
|
||||
return byteOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numBits number of bits to read
|
||||
* @return int representing the bits read. The bits will appear as the least-significant
|
||||
* bits of the int
|
||||
* @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available
|
||||
*/
|
||||
public int readBits(int numBits) {
|
||||
if (numBits < 1 || numBits > 32 || numBits > available()) {
|
||||
throw new IllegalArgumentException(String.valueOf(numBits));
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
|
||||
// First, read remainder from current byte
|
||||
if (bitOffset > 0) {
|
||||
int bitsLeft = 8 - bitOffset;
|
||||
int toRead = Math.min(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
public int available() {
|
||||
return 8 * (bytes.length - byteOffset) - bitOffset;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
|
||||
* of ISO 18004.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum CharacterSetECI {
|
||||
|
||||
// Enum name is a Java encoding valid for java.lang and java.io
|
||||
Cp437(new int[]{0,2}),
|
||||
ISO8859_1(new int[]{1,3}, "ISO-8859-1"),
|
||||
ISO8859_2(4, "ISO-8859-2"),
|
||||
ISO8859_3(5, "ISO-8859-3"),
|
||||
ISO8859_4(6, "ISO-8859-4"),
|
||||
ISO8859_5(7, "ISO-8859-5"),
|
||||
ISO8859_6(8, "ISO-8859-6"),
|
||||
ISO8859_7(9, "ISO-8859-7"),
|
||||
ISO8859_8(10, "ISO-8859-8"),
|
||||
ISO8859_9(11, "ISO-8859-9"),
|
||||
ISO8859_10(12, "ISO-8859-10"),
|
||||
ISO8859_11(13, "ISO-8859-11"),
|
||||
ISO8859_13(15, "ISO-8859-13"),
|
||||
ISO8859_14(16, "ISO-8859-14"),
|
||||
ISO8859_15(17, "ISO-8859-15"),
|
||||
ISO8859_16(18, "ISO-8859-16"),
|
||||
SJIS(20, "Shift_JIS"),
|
||||
Cp1250(21, "windows-1250"),
|
||||
Cp1251(22, "windows-1251"),
|
||||
Cp1252(23, "windows-1252"),
|
||||
Cp1256(24, "windows-1256"),
|
||||
UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"),
|
||||
UTF8(26, "UTF-8"),
|
||||
ASCII(new int[] {27, 170}, "US-ASCII"),
|
||||
Big5(28),
|
||||
GB18030(29, "GB2312", "EUC_CN", "GBK"),
|
||||
EUC_KR(30, "EUC-KR");
|
||||
|
||||
private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();
|
||||
private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();
|
||||
static {
|
||||
for (CharacterSetECI eci : values()) {
|
||||
for (int value : eci.values) {
|
||||
VALUE_TO_ECI.put(value, eci);
|
||||
}
|
||||
NAME_TO_ECI.put(eci.name(), eci);
|
||||
for (String name : eci.otherEncodingNames) {
|
||||
NAME_TO_ECI.put(name, eci);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int[] values;
|
||||
private final String[] otherEncodingNames;
|
||||
|
||||
CharacterSetECI(int value) {
|
||||
this(new int[] {value});
|
||||
}
|
||||
|
||||
CharacterSetECI(int value, String... otherEncodingNames) {
|
||||
this.values = new int[] {value};
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
CharacterSetECI(int[] values, String... otherEncodingNames) {
|
||||
this.values = values;
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value character set ECI value
|
||||
* @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but
|
||||
* unsupported
|
||||
* @throws FormatException if ECI value is invalid
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException {
|
||||
if (value < 0 || value >= 900) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
return VALUE_TO_ECI.get(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name character set ECI encoding name
|
||||
* @return CharacterSetECI representing ECI for character encoding, or null if it is legal
|
||||
* but unsupported
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByName(String name) {
|
||||
return NAME_TO_ECI.get(name);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of decoding a matrix of bits. This typically
|
||||
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
|
||||
* as well as a String interpretation of those bytes, if applicable.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class DecoderResult {
|
||||
|
||||
private final byte[] rawBytes;
|
||||
private int numBits;
|
||||
private final String text;
|
||||
private final List<byte[]> byteSegments;
|
||||
private final String ecLevel;
|
||||
private Integer errorsCorrected;
|
||||
private Integer erasures;
|
||||
private Object other;
|
||||
private final int structuredAppendParity;
|
||||
private final int structuredAppendSequenceNumber;
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel) {
|
||||
this(rawBytes, text, byteSegments, ecLevel, -1, -1);
|
||||
}
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel,
|
||||
int saSequence,
|
||||
int saParity) {
|
||||
this.rawBytes = rawBytes;
|
||||
this.numBits = rawBytes == null ? 0 : 8 * rawBytes.length;
|
||||
this.text = text;
|
||||
this.byteSegments = byteSegments;
|
||||
this.ecLevel = ecLevel;
|
||||
this.structuredAppendParity = saParity;
|
||||
this.structuredAppendSequenceNumber = saSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes representing the result, or {@code null} if not applicable
|
||||
*/
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public int getNumBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numBits overrides the number of bits that are valid in {@link #getRawBytes()}
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public void setNumBits(int numBits) {
|
||||
this.numBits = numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return text representation of the result
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of byte segments in the result, or {@code null} if not applicable
|
||||
*/
|
||||
public List<byte[]> getByteSegments() {
|
||||
return byteSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return name of error correction level used, or {@code null} if not applicable
|
||||
*/
|
||||
public String getECLevel() {
|
||||
return ecLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of errors corrected, or {@code null} if not applicable
|
||||
*/
|
||||
public Integer getErrorsCorrected() {
|
||||
return errorsCorrected;
|
||||
}
|
||||
|
||||
public void setErrorsCorrected(Integer errorsCorrected) {
|
||||
this.errorsCorrected = errorsCorrected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of erasures corrected, or {@code null} if not applicable
|
||||
*/
|
||||
public Integer getErasures() {
|
||||
return erasures;
|
||||
}
|
||||
|
||||
public void setErasures(Integer erasures) {
|
||||
this.erasures = erasures;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return arbitrary additional metadata
|
||||
*/
|
||||
public Object getOther() {
|
||||
return other;
|
||||
}
|
||||
|
||||
public void setOther(Object other) {
|
||||
this.other = other;
|
||||
}
|
||||
|
||||
public boolean hasStructuredAppend() {
|
||||
return structuredAppendParity >= 0 && structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
|
||||
public int getStructuredAppendParity() {
|
||||
return structuredAppendParity;
|
||||
}
|
||||
|
||||
public int getStructuredAppendSequenceNumber() {
|
||||
return structuredAppendSequenceNumber;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class DefaultGridSampler extends GridSampler {
|
||||
|
||||
@Override
|
||||
public BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
float p1ToX, float p1ToY,
|
||||
float p2ToX, float p2ToY,
|
||||
float p3ToX, float p3ToY,
|
||||
float p4ToX, float p4ToY,
|
||||
float p1FromX, float p1FromY,
|
||||
float p2FromX, float p2FromY,
|
||||
float p3FromX, float p3FromY,
|
||||
float p4FromX, float p4FromY) throws NotFoundException {
|
||||
|
||||
PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
|
||||
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
|
||||
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
|
||||
|
||||
return sampleGrid(image, dimensionX, dimensionY, transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
PerspectiveTransform transform) throws NotFoundException {
|
||||
if (dimensionX <= 0 || dimensionY <= 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
BitMatrix bits = new BitMatrix(dimensionX, dimensionY, 1);
|
||||
float[] points = new float[2 * dimensionX];
|
||||
for (int y = 0; y < dimensionY; y++) {
|
||||
int max = points.length;
|
||||
float iValue = y + 0.5f;
|
||||
for (int x = 0; x < max; x += 2) {
|
||||
points[x] = (float) (x / 2) + 0.5f;
|
||||
points[x + 1] = iValue;
|
||||
}
|
||||
transform.transformPoints(points);
|
||||
// Quick check to see if points transformed to something inside the image;
|
||||
// sufficient to check the endpoints
|
||||
checkAndNudgePoints(image, points);
|
||||
try {
|
||||
for (int x = 0; x < max; x += 2) {
|
||||
if (image.get((int) points[x], (int) points[x + 1])) {
|
||||
// Black(-ish) pixel
|
||||
bits.set(x / 2, y);
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException aioobe) {
|
||||
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
||||
// transform gets "twisted" such that it maps a straight line of points to a set of points
|
||||
// whose endpoints are in bounds, but others are not. There is probably some mathematical
|
||||
// way to detect this about the transformation that I don't know yet.
|
||||
// This results in an ugly runtime exception despite our clever checks above -- can't have
|
||||
// that. We could check each point's coordinates but that feels duplicative. We settle for
|
||||
// catching and wrapping ArrayIndexOutOfBoundsException.
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
/**
|
||||
* <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 Sean Owen
|
||||
*/
|
||||
public class DetectorResult {
|
||||
|
||||
private final BitMatrix bits;
|
||||
private final ResultPoint[] points;
|
||||
|
||||
public DetectorResult(BitMatrix bits, ResultPoint[] points) {
|
||||
this.bits = bits;
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
public final BitMatrix getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
public final ResultPoint[] getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.Binarizer;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.NotFoundException;
|
||||
|
||||
/**
|
||||
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
|
||||
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
|
||||
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
|
||||
* and gradients.
|
||||
*
|
||||
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public class GlobalHistogramBinarizer extends Binarizer {
|
||||
|
||||
private static final int LUMINANCE_BITS = 5;
|
||||
private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
|
||||
private byte[] luminances;
|
||||
private final int[] buckets;
|
||||
|
||||
public GlobalHistogramBinarizer(LuminanceSource source) {
|
||||
super(source);
|
||||
luminances = EMPTY;
|
||||
buckets = new int[LUMINANCE_BUCKETS];
|
||||
}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||
@Override
|
||||
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
|
||||
LuminanceSource source = getLuminanceSource();
|
||||
int width = source.getWidth();
|
||||
if (row == null || row.getSize() < width) {
|
||||
row = new BitArray(width);
|
||||
} else {
|
||||
row.clear();
|
||||
}
|
||||
|
||||
initArrays(width);
|
||||
byte[] localLuminances = source.getRow(y, luminances);
|
||||
int[] localBuckets = buckets;
|
||||
for (int x = 0; x < width; x++) {
|
||||
localBuckets[(localLuminances[x] & 0xff) >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
int blackPoint = estimateBlackPoint(localBuckets);
|
||||
|
||||
if (width < 3) {
|
||||
// Special case for very small images
|
||||
for (int x = 0; x < width; x++) {
|
||||
if ((localLuminances[x] & 0xff) < blackPoint) {
|
||||
row.set(x);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int left = localLuminances[0] & 0xff;
|
||||
int center = localLuminances[1] & 0xff;
|
||||
for (int x = 1; x < width - 1; x++) {
|
||||
int right = localLuminances[x + 1] & 0xff;
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
if (((center * 4) - left - right) / 2 < blackPoint) {
|
||||
row.set(x);
|
||||
}
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
|
||||
@Override
|
||||
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||
LuminanceSource source = getLuminanceSource();
|
||||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
BitMatrix matrix = new BitMatrix(width, height, 1);
|
||||
|
||||
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||
initArrays(width);
|
||||
int[] localBuckets = buckets;
|
||||
for (int y = 1; y < 5; y++) {
|
||||
int row = height * y / 5;
|
||||
byte[] localLuminances = source.getRow(row, luminances);
|
||||
int right = (width * 4) / 5;
|
||||
for (int x = width / 5; x < right; x++) {
|
||||
int pixel = localLuminances[x] & 0xff;
|
||||
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
}
|
||||
int blackPoint = estimateBlackPoint(localBuckets);
|
||||
|
||||
// We delay reading the entire image luminance until the black point estimation succeeds.
|
||||
// Although we end up reading four rows twice, it is consistent with our motto of
|
||||
// "fail quickly" which is necessary for continuous scanning.
|
||||
byte[] localLuminances = source.getMatrix();
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = localLuminances[offset + x] & 0xff;
|
||||
if (pixel < blackPoint) {
|
||||
matrix.set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Binarizer createBinarizer(LuminanceSource source) {
|
||||
return new GlobalHistogramBinarizer(source);
|
||||
}
|
||||
|
||||
private void initArrays(int luminanceSize) {
|
||||
if (luminances.length < luminanceSize) {
|
||||
luminances = new byte[luminanceSize];
|
||||
}
|
||||
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
|
||||
buckets[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int estimateBlackPoint(int[] buckets) throws NotFoundException {
|
||||
// Find the tallest peak in the histogram.
|
||||
int numBuckets = buckets.length;
|
||||
int maxBucketCount = 0;
|
||||
int firstPeak = 0;
|
||||
int firstPeakSize = 0;
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
if (buckets[x] > firstPeakSize) {
|
||||
firstPeak = x;
|
||||
firstPeakSize = buckets[x];
|
||||
}
|
||||
if (buckets[x] > maxBucketCount) {
|
||||
maxBucketCount = buckets[x];
|
||||
}
|
||||
}
|
||||
|
||||
// Find the second-tallest peak which is somewhat far from the tallest peak.
|
||||
int secondPeak = 0;
|
||||
int secondPeakScore = 0;
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
int distanceToBiggest = x - firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of distance.
|
||||
int score = buckets[x] * distanceToBiggest * distanceToBiggest;
|
||||
if (score > secondPeakScore) {
|
||||
secondPeak = x;
|
||||
secondPeakScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure firstPeak corresponds to the black peak.
|
||||
if (firstPeak > secondPeak) {
|
||||
int temp = firstPeak;
|
||||
firstPeak = secondPeak;
|
||||
secondPeak = temp;
|
||||
}
|
||||
|
||||
// If there is too little contrast in the image to pick a meaningful black point, throw rather
|
||||
// than waste time trying to decode the image, and risk false positives.
|
||||
if (secondPeak - firstPeak <= numBuckets / 16) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak.
|
||||
int bestValley = secondPeak - 1;
|
||||
int bestValleyScore = -1;
|
||||
for (int x = secondPeak - 1; x > firstPeak; x--) {
|
||||
int fromFirst = x - firstPeak;
|
||||
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
|
||||
if (score > bestValleyScore) {
|
||||
bestValley = x;
|
||||
bestValleyScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
return bestValley << LUMINANCE_SHIFT;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
|
||||
/**
|
||||
* 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 #setGridSampler(GridSampler)}
|
||||
* with an instance of a class which implements this interface.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public abstract class GridSampler {
|
||||
|
||||
private static GridSampler gridSampler = new DefaultGridSampler();
|
||||
|
||||
/**
|
||||
* Sets the implementation of GridSampler used by the library. One global
|
||||
* instance is stored, which may sound problematic. But, the implementation provided
|
||||
* ought to be appropriate for the entire platform, and all uses of this library
|
||||
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
|
||||
* an implementation that takes advantage of native platform libraries.
|
||||
*
|
||||
* @param newGridSampler The platform-specific object to install.
|
||||
*/
|
||||
public static void setGridSampler(GridSampler newGridSampler) {
|
||||
gridSampler = newGridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current implementation of GridSampler
|
||||
*/
|
||||
public static GridSampler getInstance() {
|
||||
return gridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling
|
||||
* transformation is determined by the coordinates of 4 points, in the original and transformed
|
||||
* image space.
|
||||
*
|
||||
* @param image image to sample
|
||||
* @param dimensionX width of {@link BitMatrix} to sample from image
|
||||
* @param dimensionY height of {@link BitMatrix} to sample from image
|
||||
* @param p1ToX point 1 preimage X
|
||||
* @param p1ToY point 1 preimage Y
|
||||
* @param p2ToX point 2 preimage X
|
||||
* @param p2ToY point 2 preimage Y
|
||||
* @param p3ToX point 3 preimage X
|
||||
* @param p3ToY point 3 preimage Y
|
||||
* @param p4ToX point 4 preimage X
|
||||
* @param p4ToY point 4 preimage Y
|
||||
* @param p1FromX point 1 image X
|
||||
* @param p1FromY point 1 image Y
|
||||
* @param p2FromX point 2 image X
|
||||
* @param p2FromY point 2 image Y
|
||||
* @param p3FromX point 3 image X
|
||||
* @param p3FromY point 3 image Y
|
||||
* @param p4FromX point 4 image X
|
||||
* @param p4FromY point 4 image Y
|
||||
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
|
||||
* defined by the "from" parameters
|
||||
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined
|
||||
* by the given points is invalid or results in sampling outside the image boundaries
|
||||
*/
|
||||
public abstract BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
float p1ToX, float p1ToY,
|
||||
float p2ToX, float p2ToY,
|
||||
float p3ToX, float p3ToY,
|
||||
float p4ToX, float p4ToY,
|
||||
float p1FromX, float p1FromY,
|
||||
float p2FromX, float p2FromY,
|
||||
float p3FromX, float p3FromY,
|
||||
float p4FromX, float p4FromY) throws NotFoundException;
|
||||
|
||||
public abstract BitMatrix sampleGrid(BitMatrix image,
|
||||
int dimensionX,
|
||||
int dimensionY,
|
||||
PerspectiveTransform transform) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* <p>Checks a set of points that have been transformed to sample points on an image against
|
||||
* the image's dimensions to see if the point are even within the image.</p>
|
||||
*
|
||||
* <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
|
||||
* barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
|
||||
* patterns in an image where the QR Code runs all the way to the image border.</p>
|
||||
*
|
||||
* <p>For efficiency, the method will check points from either end of the line until one is found
|
||||
* to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
|
||||
*
|
||||
* @param image image into which the points should map
|
||||
* @param points actual points in x1,y1,...,xn,yn form
|
||||
* @throws NotFoundException if an endpoint is lies outside the image boundaries
|
||||
*/
|
||||
protected static void checkAndNudgePoints(BitMatrix image,
|
||||
float[] points) throws NotFoundException {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
// Check and nudge points from start until we see some that are OK:
|
||||
boolean nudged = true;
|
||||
int maxOffset = points.length - 1; // points.length must be even
|
||||
for (int offset = 0; offset < maxOffset && nudged; offset += 2) {
|
||||
int x = (int) points[offset];
|
||||
int y = (int) points[offset + 1];
|
||||
if (x < -1 || x > width || y < -1 || y > height) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
nudged = false;
|
||||
if (x == -1) {
|
||||
points[offset] = 0.0f;
|
||||
nudged = true;
|
||||
} else if (x == width) {
|
||||
points[offset] = width - 1;
|
||||
nudged = true;
|
||||
}
|
||||
if (y == -1) {
|
||||
points[offset + 1] = 0.0f;
|
||||
nudged = true;
|
||||
} else if (y == height) {
|
||||
points[offset + 1] = height - 1;
|
||||
nudged = true;
|
||||
}
|
||||
}
|
||||
// Check and nudge points from end:
|
||||
nudged = true;
|
||||
for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
|
||||
int x = (int) points[offset];
|
||||
int y = (int) points[offset + 1];
|
||||
if (x < -1 || x > width || y < -1 || y > height) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
nudged = false;
|
||||
if (x == -1) {
|
||||
points[offset] = 0.0f;
|
||||
nudged = true;
|
||||
} else if (x == width) {
|
||||
points[offset] = width - 1;
|
||||
nudged = true;
|
||||
}
|
||||
if (y == -1) {
|
||||
points[offset + 1] = 0.0f;
|
||||
nudged = true;
|
||||
} else if (y == height) {
|
||||
points[offset + 1] = height - 1;
|
||||
nudged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.Binarizer;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.NotFoundException;
|
||||
|
||||
/**
|
||||
* This class implements a local thresholding algorithm, which while slower than the
|
||||
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
|
||||
* high frequency images of barcodes with black data on white backgrounds. For this application,
|
||||
* it does a much better job than a global blackpoint with severe shadows and gradients.
|
||||
* However it tends to produce artifacts on lower frequency images and is therefore not
|
||||
* a good general purpose binarizer for uses outside ZXing.
|
||||
*
|
||||
* This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
|
||||
* and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
|
||||
* inherently local, and only fails for horizontal gradients. We can revisit that problem later,
|
||||
* but for now it was not a win to use local blocks for 1D.
|
||||
*
|
||||
* This Binarizer is the default for the unit tests and the recommended class for library users.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class HybridBinarizer extends GlobalHistogramBinarizer {
|
||||
|
||||
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
|
||||
// So this is the smallest dimension in each axis we can accept.
|
||||
private static final int BLOCK_SIZE_POWER = 3;
|
||||
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00
|
||||
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11
|
||||
private static final int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
|
||||
private static final int MIN_DYNAMIC_RANGE = 24;
|
||||
|
||||
private BitMatrix matrix;
|
||||
|
||||
public HybridBinarizer(LuminanceSource source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the final BitMatrix once for all requests. This could be called once from the
|
||||
* constructor instead, but there are some advantages to doing it lazily, such as making
|
||||
* profiling easier, and not doing heavy lifting when callers don't expect it.
|
||||
*/
|
||||
@Override
|
||||
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||
if (matrix != null) {
|
||||
return matrix;
|
||||
}
|
||||
LuminanceSource source = getLuminanceSource();
|
||||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {
|
||||
byte[] luminances = source.getMatrix();
|
||||
int subWidth = width >> BLOCK_SIZE_POWER;
|
||||
if ((width & BLOCK_SIZE_MASK) != 0) {
|
||||
subWidth++;
|
||||
}
|
||||
int subHeight = height >> BLOCK_SIZE_POWER;
|
||||
if ((height & BLOCK_SIZE_MASK) != 0) {
|
||||
subHeight++;
|
||||
}
|
||||
int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
|
||||
|
||||
BitMatrix newMatrix = new BitMatrix(width, height, 1);
|
||||
calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix);
|
||||
matrix = newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
matrix = super.getBlackMatrix();
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Binarizer createBinarizer(LuminanceSource source) {
|
||||
return new HybridBinarizer(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* For each block in the image, calculate the average black point using a 5x5 grid
|
||||
* of the blocks around it. Also handles the corner cases (fractional blocks are computed based
|
||||
* on the last pixels in the row/column which are also used in the previous block).
|
||||
*/
|
||||
private static void calculateThresholdForBlock(byte[] luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height,
|
||||
int[][] blackPoints,
|
||||
BitMatrix matrix) {
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
int maxXOffset = width - BLOCK_SIZE;
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << BLOCK_SIZE_POWER;
|
||||
if (yoffset > maxYOffset) {
|
||||
yoffset = maxYOffset;
|
||||
}
|
||||
int top = cap(y, subHeight - 3);
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << BLOCK_SIZE_POWER;
|
||||
if (xoffset > maxXOffset) {
|
||||
xoffset = maxXOffset;
|
||||
}
|
||||
int left = cap(x, subWidth - 3);
|
||||
int sum = 0;
|
||||
for (int z = -2; z <= 2; z++) {
|
||||
int[] blackRow = blackPoints[top + z];
|
||||
sum += blackRow[left - 2] + blackRow[left - 1] + blackRow[left] + blackRow[left + 1] + blackRow[left + 2];
|
||||
}
|
||||
int average = sum / 25;
|
||||
thresholdBlock(luminances, xoffset, yoffset, average, width, matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int cap(int value, int max) {
|
||||
return value < 2 ? 2 : Math.min(value, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a single threshold to a block of pixels.
|
||||
*/
|
||||
private static void thresholdBlock(byte[] luminances,
|
||||
int xoffset,
|
||||
int yoffset,
|
||||
int threshold,
|
||||
int stride,
|
||||
BitMatrix matrix) {
|
||||
for (int y = 0, offset = yoffset * stride + xoffset; y < BLOCK_SIZE; y++, offset += stride) {
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if ((luminances[offset + x] & 0xFF) <= threshold) {
|
||||
matrix.set(xoffset + x, yoffset + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a single black point for each block of pixels and saves it away.
|
||||
* See the following thread for a discussion of this algorithm:
|
||||
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
*/
|
||||
private static int[][] calculateBlackPoints(byte[] luminances,
|
||||
int subWidth,
|
||||
int subHeight,
|
||||
int width,
|
||||
int height) {
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
int maxXOffset = width - BLOCK_SIZE;
|
||||
int[][] blackPoints = new int[subHeight][subWidth];
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << BLOCK_SIZE_POWER;
|
||||
if (yoffset > maxYOffset) {
|
||||
yoffset = maxYOffset;
|
||||
}
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << BLOCK_SIZE_POWER;
|
||||
if (xoffset > maxXOffset) {
|
||||
xoffset = maxXOffset;
|
||||
}
|
||||
int sum = 0;
|
||||
int min = 0xFF;
|
||||
int max = 0;
|
||||
for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
||||
int pixel = luminances[offset + xx] & 0xFF;
|
||||
sum += pixel;
|
||||
// still looking for good contrast
|
||||
if (pixel < min) {
|
||||
min = pixel;
|
||||
}
|
||||
if (pixel > max) {
|
||||
max = pixel;
|
||||
}
|
||||
}
|
||||
// short-circuit min/max tests once dynamic range is met
|
||||
if (max - min > MIN_DYNAMIC_RANGE) {
|
||||
// finish the rest of the rows quickly
|
||||
for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
||||
sum += luminances[offset + xx] & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default estimate is the average of the values in the block.
|
||||
int average = sum >> (BLOCK_SIZE_POWER * 2);
|
||||
if (max - min <= MIN_DYNAMIC_RANGE) {
|
||||
// If variation within the block is low, assume this is a block with only light or only
|
||||
// dark pixels. In that case we do not want to use the average, as it would divide this
|
||||
// low contrast area into black and white pixels, essentially creating data out of noise.
|
||||
//
|
||||
// The default assumption is that the block is light/background. Since no estimate for
|
||||
// the level of dark pixels exists locally, use half the min for the block.
|
||||
average = min / 2;
|
||||
|
||||
if (y > 0 && x > 0) {
|
||||
// Correct the "white background" assumption for blocks that have neighbors by comparing
|
||||
// the pixels in this block to the previously calculated black points. This is based on
|
||||
// the fact that dark barcode symbology is always surrounded by some amount of light
|
||||
// background for which reasonable black point estimates were made. The bp estimated at
|
||||
// the boundaries is used for the interior.
|
||||
|
||||
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||
int averageNeighborBlackPoint =
|
||||
(blackPoints[y - 1][x] + (2 * blackPoints[y][x - 1]) + blackPoints[y - 1][x - 1]) / 4;
|
||||
if (min < averageNeighborBlackPoint) {
|
||||
average = averageNeighborBlackPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
blackPoints[y][x] = average;
|
||||
}
|
||||
}
|
||||
return blackPoints;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
/**
|
||||
* <p>This class implements a perspective transform in two dimensions. Given four source and four
|
||||
* destination points, it will compute the transformation implied between them. The code is based
|
||||
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class PerspectiveTransform {
|
||||
|
||||
private final float a11;
|
||||
private final float a12;
|
||||
private final float a13;
|
||||
private final float a21;
|
||||
private final float a22;
|
||||
private final float a23;
|
||||
private final float a31;
|
||||
private final float a32;
|
||||
private final float a33;
|
||||
|
||||
private PerspectiveTransform(float a11, float a21, float a31,
|
||||
float a12, float a22, float a32,
|
||||
float a13, float a23, float a33) {
|
||||
this.a11 = a11;
|
||||
this.a12 = a12;
|
||||
this.a13 = a13;
|
||||
this.a21 = a21;
|
||||
this.a22 = a22;
|
||||
this.a23 = a23;
|
||||
this.a31 = a31;
|
||||
this.a32 = a32;
|
||||
this.a33 = a33;
|
||||
}
|
||||
|
||||
public static PerspectiveTransform quadrilateralToQuadrilateral(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) {
|
||||
|
||||
PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
|
||||
PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
||||
return sToQ.times(qToS);
|
||||
}
|
||||
|
||||
public void transformPoints(float[] points) {
|
||||
float a11 = this.a11;
|
||||
float a12 = this.a12;
|
||||
float a13 = this.a13;
|
||||
float a21 = this.a21;
|
||||
float a22 = this.a22;
|
||||
float a23 = this.a23;
|
||||
float a31 = this.a31;
|
||||
float a32 = this.a32;
|
||||
float a33 = this.a33;
|
||||
int maxI = points.length - 1; // points.length must be even
|
||||
for (int i = 0; i < maxI; i += 2) {
|
||||
float x = points[i];
|
||||
float y = points[i + 1];
|
||||
float denominator = a13 * x + a23 * y + a33;
|
||||
points[i] = (a11 * x + a21 * y + a31) / denominator;
|
||||
points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
|
||||
}
|
||||
}
|
||||
|
||||
public void transformPoints(float[] xValues, float[] yValues) {
|
||||
int n = xValues.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
float x = xValues[i];
|
||||
float y = yValues[i];
|
||||
float denominator = a13 * x + a23 * y + a33;
|
||||
xValues[i] = (a11 * x + a21 * y + a31) / denominator;
|
||||
yValues[i] = (a12 * x + a22 * y + a32) / denominator;
|
||||
}
|
||||
}
|
||||
|
||||
public static PerspectiveTransform squareToQuadrilateral(float x0, float y0,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3) {
|
||||
float dx3 = x0 - x1 + x2 - x3;
|
||||
float dy3 = y0 - y1 + y2 - y3;
|
||||
if (dx3 == 0.0f && dy3 == 0.0f) {
|
||||
// Affine
|
||||
return new PerspectiveTransform(x1 - x0, x2 - x1, x0,
|
||||
y1 - y0, y2 - y1, y0,
|
||||
0.0f, 0.0f, 1.0f);
|
||||
} else {
|
||||
float dx1 = x1 - x2;
|
||||
float dx2 = x3 - x2;
|
||||
float dy1 = y1 - y2;
|
||||
float dy2 = y3 - y2;
|
||||
float denominator = dx1 * dy2 - dx2 * dy1;
|
||||
float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
|
||||
float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
|
||||
return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,
|
||||
y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,
|
||||
a13, a23, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public static PerspectiveTransform quadrilateralToSquare(float x0, float y0,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3) {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
|
||||
}
|
||||
|
||||
PerspectiveTransform buildAdjoint() {
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
return new PerspectiveTransform(a22 * a33 - a23 * a32,
|
||||
a23 * a31 - a21 * a33,
|
||||
a21 * a32 - a22 * a31,
|
||||
a13 * a32 - a12 * a33,
|
||||
a11 * a33 - a13 * a31,
|
||||
a12 * a31 - a11 * a32,
|
||||
a12 * a23 - a13 * a22,
|
||||
a13 * a21 - a11 * a23,
|
||||
a11 * a22 - a12 * a21);
|
||||
}
|
||||
|
||||
PerspectiveTransform times(PerspectiveTransform other) {
|
||||
return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
|
||||
a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
|
||||
a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
|
||||
a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
|
||||
a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
|
||||
a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
|
||||
a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
|
||||
a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
|
||||
a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
|
||||
/**
|
||||
* Common string-related functions.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Alex Dupre
|
||||
*/
|
||||
public final class StringUtils {
|
||||
|
||||
private static final String PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset().name();
|
||||
public static final String SHIFT_JIS = "SJIS";
|
||||
public static final String GB2312 = "GB2312";
|
||||
private static final String EUC_JP = "EUC_JP";
|
||||
private static final String UTF8 = "UTF8";
|
||||
private static final String ISO88591 = "ISO8859_1";
|
||||
private static final boolean ASSUME_SHIFT_JIS =
|
||||
SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||
|
||||
EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);
|
||||
|
||||
private StringUtils() { }
|
||||
|
||||
/**
|
||||
* @param bytes bytes encoding a string, whose encoding should be guessed
|
||||
* @param hints decode hints if applicable
|
||||
* @return name of guessed encoding; at the moment will only guess one of:
|
||||
* {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
|
||||
* default encoding if none of these can possibly be correct
|
||||
*/
|
||||
public static String guessEncoding(byte[] bytes, Map<DecodeHintType,?> hints) {
|
||||
if (hints != null && hints.containsKey(DecodeHintType.CHARACTER_SET)) {
|
||||
return hints.get(DecodeHintType.CHARACTER_SET).toString();
|
||||
}
|
||||
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
||||
// which should be by far the most common encodings.
|
||||
int length = bytes.length;
|
||||
boolean canBeISO88591 = true;
|
||||
boolean canBeShiftJIS = true;
|
||||
boolean canBeUTF8 = true;
|
||||
int utf8BytesLeft = 0;
|
||||
int utf2BytesChars = 0;
|
||||
int utf3BytesChars = 0;
|
||||
int utf4BytesChars = 0;
|
||||
int sjisBytesLeft = 0;
|
||||
int sjisKatakanaChars = 0;
|
||||
int sjisCurKatakanaWordLength = 0;
|
||||
int sjisCurDoubleBytesWordLength = 0;
|
||||
int sjisMaxKatakanaWordLength = 0;
|
||||
int sjisMaxDoubleBytesWordLength = 0;
|
||||
int isoHighOther = 0;
|
||||
|
||||
boolean utf8bom = bytes.length > 3 &&
|
||||
bytes[0] == (byte) 0xEF &&
|
||||
bytes[1] == (byte) 0xBB &&
|
||||
bytes[2] == (byte) 0xBF;
|
||||
|
||||
for (int i = 0;
|
||||
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
|
||||
i++) {
|
||||
|
||||
int value = bytes[i] & 0xFF;
|
||||
|
||||
// UTF-8 stuff
|
||||
if (canBeUTF8) {
|
||||
if (utf8BytesLeft > 0) {
|
||||
if ((value & 0x80) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft--;
|
||||
}
|
||||
} else if ((value & 0x80) != 0) {
|
||||
if ((value & 0x40) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x20) == 0) {
|
||||
utf2BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x10) == 0) {
|
||||
utf3BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x08) == 0) {
|
||||
utf4BytesChars++;
|
||||
} else {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ISO-8859-1 stuff
|
||||
if (canBeISO88591) {
|
||||
if (value > 0x7F && value < 0xA0) {
|
||||
canBeISO88591 = false;
|
||||
} else if (value > 0x9F && (value < 0xC0 || value == 0xD7 || value == 0xF7)) {
|
||||
isoHighOther++;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift_JIS stuff
|
||||
if (canBeShiftJIS) {
|
||||
if (sjisBytesLeft > 0) {
|
||||
if (value < 0x40 || value == 0x7F || value > 0xFC) {
|
||||
canBeShiftJIS = false;
|
||||
} else {
|
||||
sjisBytesLeft--;
|
||||
}
|
||||
} else if (value == 0x80 || value == 0xA0 || value > 0xEF) {
|
||||
canBeShiftJIS = false;
|
||||
} else if (value > 0xA0 && value < 0xE0) {
|
||||
sjisKatakanaChars++;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
sjisCurKatakanaWordLength++;
|
||||
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {
|
||||
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;
|
||||
}
|
||||
} else if (value > 0x7F) {
|
||||
sjisBytesLeft++;
|
||||
//sjisDoubleBytesChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength++;
|
||||
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {
|
||||
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;
|
||||
}
|
||||
} else {
|
||||
//sjisLowChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canBeUTF8 && utf8BytesLeft > 0) {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
if (canBeShiftJIS && sjisBytesLeft > 0) {
|
||||
canBeShiftJIS = false;
|
||||
}
|
||||
|
||||
// Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done
|
||||
if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {
|
||||
return UTF8;
|
||||
}
|
||||
// Easy -- if assuming Shift_JIS or >= 3 valid consecutive not-ascii characters (and no evidence it can't be), done
|
||||
if (canBeShiftJIS && (ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:
|
||||
// - If we saw
|
||||
// - only two consecutive katakana chars in the whole text, or
|
||||
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
|
||||
// - then we conclude Shift_JIS, else ISO-8859-1
|
||||
if (canBeISO88591 && canBeShiftJIS) {
|
||||
return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= length
|
||||
? SHIFT_JIS : ISO88591;
|
||||
}
|
||||
|
||||
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding
|
||||
if (canBeISO88591) {
|
||||
return ISO88591;
|
||||
}
|
||||
if (canBeShiftJIS) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
if (canBeUTF8) {
|
||||
return UTF8;
|
||||
}
|
||||
// Otherwise, we take a wild guess with platform encoding
|
||||
return PLATFORM_DEFAULT_ENCODING;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.detector;
|
||||
|
||||
/**
|
||||
* General math-related and numeric utility functions.
|
||||
*/
|
||||
public final class MathUtils {
|
||||
|
||||
private MathUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
|
||||
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
|
||||
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
|
||||
*
|
||||
* @param d real value to round
|
||||
* @return nearest {@code int}
|
||||
*/
|
||||
public static int round(float d) {
|
||||
return (int) (d + (d < 0.0f ? -0.5f : 0.5f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aX point A x coordinate
|
||||
* @param aY point A y coordinate
|
||||
* @param bX point B x coordinate
|
||||
* @param bY point B y coordinate
|
||||
* @return Euclidean distance between points A and B
|
||||
*/
|
||||
public static float distance(float aX, float aY, float bX, float bY) {
|
||||
double xDiff = aX - bX;
|
||||
double yDiff = aY - bY;
|
||||
return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aX point A x coordinate
|
||||
* @param aY point A y coordinate
|
||||
* @param bX point B x coordinate
|
||||
* @param bY point B y coordinate
|
||||
* @return Euclidean distance between points A and B
|
||||
*/
|
||||
public static float distance(int aX, int aY, int bX, int bY) {
|
||||
double xDiff = aX - bX;
|
||||
double yDiff = aY - bY;
|
||||
return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array values to sum
|
||||
* @return sum of values in array
|
||||
*/
|
||||
public static int sum(int[] array) {
|
||||
int count = 0;
|
||||
for (int a : array) {
|
||||
count += a;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.detector;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
|
||||
* It looks within a mostly white region of an image for a region of black and white, but mostly
|
||||
* black. It returns the four corners of the region, as best it can determine.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @deprecated without replacement since 3.3.0
|
||||
*/
|
||||
@Deprecated
|
||||
public final class MonochromeRectangleDetector {
|
||||
|
||||
private static final int MAX_MODULES = 32;
|
||||
|
||||
private final BitMatrix image;
|
||||
|
||||
public MonochromeRectangleDetector(BitMatrix image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||
* white, in an image.</p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
|
||||
* last points are opposed on the diagonal, as are the second and third. The first point will be
|
||||
* the topmost point and the last, the bottommost. The second point will be leftmost and the
|
||||
* third, the rightmost
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
public ResultPoint[] detect() throws NotFoundException {
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
int halfHeight = height / 2;
|
||||
int halfWidth = width / 2;
|
||||
int deltaY = Math.max(1, height / (MAX_MODULES * 8));
|
||||
int deltaX = Math.max(1, width / (MAX_MODULES * 8));
|
||||
|
||||
int top = 0;
|
||||
int bottom = height;
|
||||
int left = 0;
|
||||
int right = width;
|
||||
ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right,
|
||||
halfHeight, -deltaY, top, bottom, halfWidth / 2);
|
||||
top = (int) pointA.getY() - 1;
|
||||
ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right,
|
||||
halfHeight, 0, top, bottom, halfHeight / 2);
|
||||
left = (int) pointB.getX() - 1;
|
||||
ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right,
|
||||
halfHeight, 0, top, bottom, halfHeight / 2);
|
||||
right = (int) pointC.getX() + 1;
|
||||
ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right,
|
||||
halfHeight, deltaY, top, bottom, halfWidth / 2);
|
||||
bottom = (int) pointD.getY() + 1;
|
||||
|
||||
// Go try to find point A again with better information -- might have been off at first.
|
||||
pointA = findCornerFromCenter(halfWidth, 0, left, right,
|
||||
halfHeight, -deltaY, top, bottom, halfWidth / 4);
|
||||
|
||||
return new ResultPoint[] { pointA, pointB, pointC, pointD };
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||
* point which should be within the barcode.
|
||||
*
|
||||
* @param centerX center's x component (horizontal)
|
||||
* @param deltaX same as deltaY but change in x per step instead
|
||||
* @param left minimum value of x
|
||||
* @param right maximum value of x
|
||||
* @param centerY center's y component (vertical)
|
||||
* @param deltaY change in y per step. If scanning up this is negative; down, positive;
|
||||
* left or right, 0
|
||||
* @param top minimum value of y to search through (meaningless when di == 0)
|
||||
* @param bottom maximum value of y
|
||||
* @param maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||
* the barcode
|
||||
* @return a {@link ResultPoint} encapsulating the corner that was found
|
||||
* @throws NotFoundException if such a point cannot be found
|
||||
*/
|
||||
private ResultPoint findCornerFromCenter(int centerX,
|
||||
int deltaX,
|
||||
int left,
|
||||
int right,
|
||||
int centerY,
|
||||
int deltaY,
|
||||
int top,
|
||||
int bottom,
|
||||
int maxWhiteRun) throws NotFoundException {
|
||||
int[] lastRange = null;
|
||||
for (int y = centerY, x = centerX;
|
||||
y < bottom && y >= top && x < right && x >= left;
|
||||
y += deltaY, x += deltaX) {
|
||||
int[] range;
|
||||
if (deltaX == 0) {
|
||||
// horizontal slices, up and down
|
||||
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
|
||||
}
|
||||
if (range == null) {
|
||||
if (lastRange == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
// lastRange was found
|
||||
if (deltaX == 0) {
|
||||
int lastY = y - deltaY;
|
||||
if (lastRange[0] < centerX) {
|
||||
if (lastRange[1] > centerX) {
|
||||
// straddle, choose one or the other based on direction
|
||||
return new ResultPoint(lastRange[deltaY > 0 ? 0 : 1], lastY);
|
||||
}
|
||||
return new ResultPoint(lastRange[0], lastY);
|
||||
} else {
|
||||
return new ResultPoint(lastRange[1], lastY);
|
||||
}
|
||||
} else {
|
||||
int lastX = x - deltaX;
|
||||
if (lastRange[0] < centerY) {
|
||||
if (lastRange[1] > centerY) {
|
||||
return new ResultPoint(lastX, lastRange[deltaX < 0 ? 0 : 1]);
|
||||
}
|
||||
return new ResultPoint(lastX, lastRange[0]);
|
||||
} else {
|
||||
return new ResultPoint(lastX, lastRange[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastRange = range;
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
|
||||
* be part of a Data Matrix barcode.
|
||||
*
|
||||
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
|
||||
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
|
||||
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
|
||||
* barcode region
|
||||
* @param minDim minimum pixel location, horizontally or vertically, to consider
|
||||
* @param maxDim maximum pixel location, horizontally or vertically, to consider
|
||||
* @param horizontal if true, we're scanning left-right, instead of up-down
|
||||
* @return int[] with start and end of found range, or null if no such range is found
|
||||
* (e.g. only white was found)
|
||||
*/
|
||||
private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, boolean horizontal) {
|
||||
|
||||
int center = (minDim + maxDim) / 2;
|
||||
|
||||
// Scan left/up first
|
||||
int start = center;
|
||||
while (start >= minDim) {
|
||||
if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) {
|
||||
start--;
|
||||
} else {
|
||||
int whiteRunStart = start;
|
||||
do {
|
||||
start--;
|
||||
} while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) :
|
||||
image.get(fixedDimension, start)));
|
||||
int whiteRunSize = whiteRunStart - start;
|
||||
if (start < minDim || whiteRunSize > maxWhiteRun) {
|
||||
start = whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
start++;
|
||||
|
||||
// Then try right/down
|
||||
int end = center;
|
||||
while (end < maxDim) {
|
||||
if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) {
|
||||
end++;
|
||||
} else {
|
||||
int whiteRunStart = end;
|
||||
do {
|
||||
end++;
|
||||
} while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) :
|
||||
image.get(fixedDimension, end)));
|
||||
int whiteRunSize = end - whiteRunStart;
|
||||
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
|
||||
end = whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
end--;
|
||||
|
||||
return end > start ? new int[]{start, end} : null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.detector;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Detects a candidate barcode-like rectangular region within an image. It
|
||||
* starts around the center of the image, increases the size of the candidate
|
||||
* region until it finds a white rectangular region. By keeping track of the
|
||||
* last black points it encountered, it determines the corners of the barcode.
|
||||
* </p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class WhiteRectangleDetector {
|
||||
|
||||
private static final int INIT_SIZE = 10;
|
||||
private static final int CORR = 1;
|
||||
|
||||
private final BitMatrix image;
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int leftInit;
|
||||
private final int rightInit;
|
||||
private final int downInit;
|
||||
private final int upInit;
|
||||
|
||||
public WhiteRectangleDetector(BitMatrix image) throws NotFoundException {
|
||||
this(image, INIT_SIZE, image.getWidth() / 2, image.getHeight() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param image barcode image to find a rectangle in
|
||||
* @param initSize initial size of search area around center
|
||||
* @param x x position of search center
|
||||
* @param y y position of search center
|
||||
* @throws NotFoundException if image is too small to accommodate {@code initSize}
|
||||
*/
|
||||
public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {
|
||||
this.image = image;
|
||||
height = image.getHeight();
|
||||
width = image.getWidth();
|
||||
int halfsize = initSize / 2;
|
||||
leftInit = x - halfsize;
|
||||
rightInit = x + halfsize;
|
||||
upInit = y - halfsize;
|
||||
downInit = y + halfsize;
|
||||
if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Detects a candidate barcode-like rectangular region within an image. It
|
||||
* starts around the center of the image, increases the size of the candidate
|
||||
* region until it finds a white rectangular region.
|
||||
* </p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular
|
||||
* region. The first and last points are opposed on the diagonal, as
|
||||
* are the second and third. The first point will be the topmost
|
||||
* point and the last, the bottommost. The second point will be
|
||||
* leftmost and the third, the rightmost
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
public ResultPoint[] detect() throws NotFoundException {
|
||||
|
||||
int left = leftInit;
|
||||
int right = rightInit;
|
||||
int up = upInit;
|
||||
int down = downInit;
|
||||
boolean sizeExceeded = false;
|
||||
boolean aBlackPointFoundOnBorder = true;
|
||||
|
||||
boolean atLeastOneBlackPointFoundOnRight = false;
|
||||
boolean atLeastOneBlackPointFoundOnBottom = false;
|
||||
boolean atLeastOneBlackPointFoundOnLeft = false;
|
||||
boolean atLeastOneBlackPointFoundOnTop = false;
|
||||
|
||||
while (aBlackPointFoundOnBorder) {
|
||||
|
||||
aBlackPointFoundOnBorder = false;
|
||||
|
||||
// .....
|
||||
// . |
|
||||
// .....
|
||||
boolean rightBorderNotWhite = true;
|
||||
while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < width) {
|
||||
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
|
||||
if (rightBorderNotWhite) {
|
||||
right++;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnRight = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnRight) {
|
||||
right++;
|
||||
}
|
||||
}
|
||||
|
||||
if (right >= width) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .....
|
||||
// . .
|
||||
// .___.
|
||||
boolean bottomBorderNotWhite = true;
|
||||
while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < height) {
|
||||
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
|
||||
if (bottomBorderNotWhite) {
|
||||
down++;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnBottom = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnBottom) {
|
||||
down++;
|
||||
}
|
||||
}
|
||||
|
||||
if (down >= height) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .....
|
||||
// | .
|
||||
// .....
|
||||
boolean leftBorderNotWhite = true;
|
||||
while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {
|
||||
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
|
||||
if (leftBorderNotWhite) {
|
||||
left--;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnLeft = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnLeft) {
|
||||
left--;
|
||||
}
|
||||
}
|
||||
|
||||
if (left < 0) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// .___.
|
||||
// . .
|
||||
// .....
|
||||
boolean topBorderNotWhite = true;
|
||||
while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {
|
||||
topBorderNotWhite = containsBlackPoint(left, right, up, true);
|
||||
if (topBorderNotWhite) {
|
||||
up--;
|
||||
aBlackPointFoundOnBorder = true;
|
||||
atLeastOneBlackPointFoundOnTop = true;
|
||||
} else if (!atLeastOneBlackPointFoundOnTop) {
|
||||
up--;
|
||||
}
|
||||
}
|
||||
|
||||
if (up < 0) {
|
||||
sizeExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!sizeExceeded) {
|
||||
|
||||
int maxSize = right - left;
|
||||
|
||||
ResultPoint z = null;
|
||||
for (int i = 1; z == null && i < maxSize; i++) {
|
||||
z = getBlackPointOnSegment(left, down - i, left + i, down);
|
||||
}
|
||||
|
||||
if (z == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
ResultPoint t = null;
|
||||
//go down right
|
||||
for (int i = 1; t == null && i < maxSize; i++) {
|
||||
t = getBlackPointOnSegment(left, up + i, left + i, up);
|
||||
}
|
||||
|
||||
if (t == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
ResultPoint x = null;
|
||||
//go down left
|
||||
for (int i = 1; x == null && i < maxSize; i++) {
|
||||
x = getBlackPointOnSegment(right, up + i, right - i, up);
|
||||
}
|
||||
|
||||
if (x == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
ResultPoint y = null;
|
||||
//go up left
|
||||
for (int i = 1; y == null && i < maxSize; i++) {
|
||||
y = getBlackPointOnSegment(right, down - i, right - i, down);
|
||||
}
|
||||
|
||||
if (y == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
return centerEdges(y, z, x, t);
|
||||
|
||||
} else {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
|
||||
int dist = MathUtils.round(MathUtils.distance(aX, aY, bX, bY));
|
||||
float xStep = (bX - aX) / dist;
|
||||
float yStep = (bY - aY) / dist;
|
||||
|
||||
for (int i = 0; i < dist; i++) {
|
||||
int x = MathUtils.round(aX + i * xStep);
|
||||
int y = MathUtils.round(aY + i * yStep);
|
||||
if (image.get(x, y)) {
|
||||
return new ResultPoint(x, y);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* recenters the points of a constant distance towards the center
|
||||
*
|
||||
* @param y bottom most point
|
||||
* @param z left most point
|
||||
* @param x right most point
|
||||
* @param t top most point
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular
|
||||
* region. The first and last points are opposed on the diagonal, as
|
||||
* are the second and third. The first point will be the topmost
|
||||
* point and the last, the bottommost. The second point will be
|
||||
* leftmost and the third, the rightmost
|
||||
*/
|
||||
private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z,
|
||||
ResultPoint x, ResultPoint t) {
|
||||
|
||||
//
|
||||
// t t
|
||||
// z x
|
||||
// x OR z
|
||||
// y y
|
||||
//
|
||||
|
||||
float yi = y.getX();
|
||||
float yj = y.getY();
|
||||
float zi = z.getX();
|
||||
float zj = z.getY();
|
||||
float xi = x.getX();
|
||||
float xj = x.getY();
|
||||
float ti = t.getX();
|
||||
float tj = t.getY();
|
||||
|
||||
if (yi < width / 2.0f) {
|
||||
return new ResultPoint[]{
|
||||
new ResultPoint(ti - CORR, tj + CORR),
|
||||
new ResultPoint(zi + CORR, zj + CORR),
|
||||
new ResultPoint(xi - CORR, xj - CORR),
|
||||
new ResultPoint(yi + CORR, yj - CORR)};
|
||||
} else {
|
||||
return new ResultPoint[]{
|
||||
new ResultPoint(ti + CORR, tj + CORR),
|
||||
new ResultPoint(zi + CORR, zj - CORR),
|
||||
new ResultPoint(xi - CORR, xj + CORR),
|
||||
new ResultPoint(yi - CORR, yj - CORR)};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a segment contains a black point
|
||||
*
|
||||
* @param a min value of the scanned coordinate
|
||||
* @param b max value of the scanned coordinate
|
||||
* @param fixed value of fixed coordinate
|
||||
* @param horizontal set to true if scan must be horizontal, false if vertical
|
||||
* @return true if a black point has been found, else false.
|
||||
*/
|
||||
private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) {
|
||||
|
||||
if (horizontal) {
|
||||
for (int x = a; x <= b; x++) {
|
||||
if (image.get(x, fixed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = a; y <= b; y++) {
|
||||
if (image.get(fixed, y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>This class contains utility methods for performing mathematical operations over
|
||||
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
|
||||
*
|
||||
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
||||
* for convenience and speed (but at the cost of memory).
|
||||
* </p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class GenericGF {
|
||||
|
||||
public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
|
||||
public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6;
|
||||
|
||||
private final int[] expTable;
|
||||
private final int[] logTable;
|
||||
private final GenericGFPoly zero;
|
||||
private final GenericGFPoly one;
|
||||
private final int size;
|
||||
private final int primitive;
|
||||
private final int generatorBase;
|
||||
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public GenericGF(int primitive, int size, int b) {
|
||||
this.primitive = primitive;
|
||||
this.size = size;
|
||||
this.generatorBase = b;
|
||||
|
||||
expTable = new int[size];
|
||||
logTable = new int[size];
|
||||
int x = 1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
expTable[i] = x;
|
||||
x *= 2; // we're assuming the generator alpha is 2
|
||||
if (x >= size) {
|
||||
x ^= primitive;
|
||||
x &= size - 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
logTable[expTable[i]] = i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero = new GenericGFPoly(this, new int[]{0});
|
||||
one = new GenericGFPoly(this, new int[]{1});
|
||||
}
|
||||
|
||||
GenericGFPoly getZero() {
|
||||
return zero;
|
||||
}
|
||||
|
||||
GenericGFPoly getOne() {
|
||||
return one;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
GenericGFPoly 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 GenericGFPoly(this, coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
static int addOrSubtract(int a, int b) {
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
int exp(int a) {
|
||||
return expTable[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
int log(int a) {
|
||||
if (a == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return logTable[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
int inverse(int a) {
|
||||
if (a == 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return expTable[size - logTable[a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return product of a and b in GF(size)
|
||||
*/
|
||||
int multiply(int a, int b) {
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getGeneratorBase() {
|
||||
return generatorBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')';
|
||||
}
|
||||
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
||||
* 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 Sean Owen
|
||||
*/
|
||||
final class GenericGFPoly {
|
||||
|
||||
private final GenericGF field;
|
||||
private final int[] coefficients;
|
||||
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients coefficients as ints representing elements of GF(size), 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")
|
||||
*/
|
||||
GenericGFPoly(GenericGF field, int[] coefficients) {
|
||||
if (coefficients.length == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.field = field;
|
||||
int coefficientsLength = coefficients.length;
|
||||
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
int firstNonZero = 1;
|
||||
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
||||
firstNonZero++;
|
||||
}
|
||||
if (firstNonZero == coefficientsLength) {
|
||||
this.coefficients = new int[]{0};
|
||||
} else {
|
||||
this.coefficients = new int[coefficientsLength - firstNonZero];
|
||||
System.arraycopy(coefficients,
|
||||
firstNonZero,
|
||||
this.coefficients,
|
||||
0,
|
||||
this.coefficients.length);
|
||||
}
|
||||
} else {
|
||||
this.coefficients = coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
int[] getCoefficients() {
|
||||
return 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 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);
|
||||
}
|
||||
if (a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
int result = 0;
|
||||
for (int coefficient : coefficients) {
|
||||
result = GenericGF.addOrSubtract(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int result = coefficients[0];
|
||||
int size = coefficients.length;
|
||||
for (int i = 1; i < size; i++) {
|
||||
result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GenericGFPoly addOrSubtract(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
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] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
return new GenericGFPoly(field, sumDiff);
|
||||
}
|
||||
|
||||
GenericGFPoly multiply(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (isZero() || other.isZero()) {
|
||||
return field.getZero();
|
||||
}
|
||||
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] = GenericGF.addOrSubtract(product[i + j],
|
||||
field.multiply(aCoeff, bCoefficients[j]));
|
||||
}
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly multiply(int scalar) {
|
||||
if (scalar == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
if (scalar == 1) {
|
||||
return this;
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], scalar);
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly multiplyByMonomial(int degree, int coefficient) {
|
||||
if (degree < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size + degree];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], coefficient);
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly[] divide(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (other.isZero()) {
|
||||
throw new IllegalArgumentException("Divide by 0");
|
||||
}
|
||||
|
||||
GenericGFPoly quotient = field.getZero();
|
||||
GenericGFPoly remainder = this;
|
||||
|
||||
int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
|
||||
int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
|
||||
|
||||
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
|
||||
int degreeDifference = remainder.getDegree() - other.getDegree();
|
||||
int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
|
||||
GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
|
||||
GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
|
||||
quotient = quotient.addOrSubtract(iterationQuotient);
|
||||
remainder = remainder.addOrSubtract(term);
|
||||
}
|
||||
|
||||
return new GenericGFPoly[] { quotient, remainder };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isZero()) {
|
||||
return "0";
|
||||
}
|
||||
StringBuilder result = new StringBuilder(8 * getDegree());
|
||||
for (int degree = getDegree(); degree >= 0; degree--) {
|
||||
int coefficient = getCoefficient(degree);
|
||||
if (coefficient != 0) {
|
||||
if (coefficient < 0) {
|
||||
if (degree == getDegree()) {
|
||||
result.append("-");
|
||||
} else {
|
||||
result.append(" - ");
|
||||
}
|
||||
coefficient = -coefficient;
|
||||
} else {
|
||||
if (result.length() > 0) {
|
||||
result.append(" + ");
|
||||
}
|
||||
}
|
||||
if (degree == 0 || coefficient != 1) {
|
||||
int alphaPower = field.log(coefficient);
|
||||
if (alphaPower == 0) {
|
||||
result.append('1');
|
||||
} else if (alphaPower == 1) {
|
||||
result.append('a');
|
||||
} else {
|
||||
result.append("a^");
|
||||
result.append(alphaPower);
|
||||
}
|
||||
}
|
||||
if (degree != 0) {
|
||||
if (degree == 1) {
|
||||
result.append('x');
|
||||
} else {
|
||||
result.append("x^");
|
||||
result.append(degree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
/**
|
||||
* <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 Sean Owen
|
||||
* @author William Rucklidge
|
||||
* @author sanfordsquires
|
||||
*/
|
||||
public final class ReedSolomonDecoder {
|
||||
|
||||
private final GenericGF field;
|
||||
|
||||
public ReedSolomonDecoder(GenericGF field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes given set of received codewords, which include both data and error-correction
|
||||
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
|
||||
* in the input.</p>
|
||||
*
|
||||
* @param received data and error-correction codewords
|
||||
* @param twoS number of error-correction codewords available
|
||||
* @throws ReedSolomonException if decoding fails for any reason
|
||||
*/
|
||||
public void decode(int[] received, int twoS) throws ReedSolomonException {
|
||||
GenericGFPoly poly = new GenericGFPoly(field, received);
|
||||
int[] syndromeCoefficients = new int[twoS];
|
||||
boolean noError = true;
|
||||
for (int i = 0; i < twoS; i++) {
|
||||
int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase()));
|
||||
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
|
||||
if (eval != 0) {
|
||||
noError = false;
|
||||
}
|
||||
}
|
||||
if (noError) {
|
||||
return;
|
||||
}
|
||||
GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients);
|
||||
GenericGFPoly[] sigmaOmega =
|
||||
runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
|
||||
GenericGFPoly sigma = sigmaOmega[0];
|
||||
GenericGFPoly omega = sigmaOmega[1];
|
||||
int[] errorLocations = findErrorLocations(sigma);
|
||||
int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations);
|
||||
for (int i = 0; i < errorLocations.length; i++) {
|
||||
int position = received.length - 1 - field.log(errorLocations[i]);
|
||||
if (position < 0) {
|
||||
throw new ReedSolomonException("Bad error location");
|
||||
}
|
||||
received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)
|
||||
throws ReedSolomonException {
|
||||
// Assume a's degree is >= b's
|
||||
if (a.getDegree() < b.getDegree()) {
|
||||
GenericGFPoly temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
GenericGFPoly rLast = a;
|
||||
GenericGFPoly r = b;
|
||||
GenericGFPoly tLast = field.getZero();
|
||||
GenericGFPoly t = field.getOne();
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while (r.getDegree() >= R / 2) {
|
||||
GenericGFPoly rLastLast = rLast;
|
||||
GenericGFPoly tLastLast = tLast;
|
||||
rLast = r;
|
||||
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;
|
||||
GenericGFPoly q = field.getZero();
|
||||
int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
|
||||
int dltInverse = field.inverse(denominatorLeadingTerm);
|
||||
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
|
||||
int degreeDiff = r.getDegree() - rLast.getDegree();
|
||||
int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
|
||||
q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
|
||||
r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
|
||||
}
|
||||
|
||||
t = q.multiply(tLast).addOrSubtract(tLastLast);
|
||||
|
||||
if (r.getDegree() >= rLast.getDegree()) {
|
||||
throw new IllegalStateException("Division algorithm failed to reduce polynomial?");
|
||||
}
|
||||
}
|
||||
|
||||
int sigmaTildeAtZero = t.getCoefficient(0);
|
||||
if (sigmaTildeAtZero == 0) {
|
||||
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||
}
|
||||
|
||||
int inverse = field.inverse(sigmaTildeAtZero);
|
||||
GenericGFPoly sigma = t.multiply(inverse);
|
||||
GenericGFPoly omega = r.multiply(inverse);
|
||||
return new GenericGFPoly[]{sigma, omega};
|
||||
}
|
||||
|
||||
private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException {
|
||||
// This is a direct application of Chien's search
|
||||
int numErrors = errorLocator.getDegree();
|
||||
if (numErrors == 1) { // shortcut
|
||||
return new int[] { errorLocator.getCoefficient(1) };
|
||||
}
|
||||
int[] result = new int[numErrors];
|
||||
int e = 0;
|
||||
for (int i = 1; i < field.getSize() && e < numErrors; i++) {
|
||||
if (errorLocator.evaluateAt(i) == 0) {
|
||||
result[e] = field.inverse(i);
|
||||
e++;
|
||||
}
|
||||
}
|
||||
if (e != numErrors) {
|
||||
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations) {
|
||||
// This is directly applying Forney's Formula
|
||||
int s = errorLocations.length;
|
||||
int[] result = new int[s];
|
||||
for (int i = 0; i < s; i++) {
|
||||
int xiInverse = field.inverse(errorLocations[i]);
|
||||
int denominator = 1;
|
||||
for (int j = 0; j < s; j++) {
|
||||
if (i != j) {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
|
||||
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
|
||||
// Below is a funny-looking workaround from Steven Parkes
|
||||
int term = field.multiply(errorLocations[j], xiInverse);
|
||||
int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
|
||||
denominator = field.multiply(denominator, termPlus1);
|
||||
}
|
||||
}
|
||||
result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
|
||||
field.inverse(denominator));
|
||||
if (field.getGeneratorBase() != 0) {
|
||||
result[i] = field.multiply(result[i], xiInverse);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Implements Reed-Solomon encoding, as the name implies.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author William Rucklidge
|
||||
*/
|
||||
public final class ReedSolomonEncoder {
|
||||
|
||||
private final GenericGF field;
|
||||
private final List<GenericGFPoly> cachedGenerators;
|
||||
|
||||
public ReedSolomonEncoder(GenericGF field) {
|
||||
this.field = field;
|
||||
this.cachedGenerators = new ArrayList<>();
|
||||
cachedGenerators.add(new GenericGFPoly(field, new int[]{1}));
|
||||
}
|
||||
|
||||
private GenericGFPoly buildGenerator(int degree) {
|
||||
if (degree >= cachedGenerators.size()) {
|
||||
GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1);
|
||||
for (int d = cachedGenerators.size(); d <= degree; d++) {
|
||||
GenericGFPoly nextGenerator = lastGenerator.multiply(
|
||||
new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) }));
|
||||
cachedGenerators.add(nextGenerator);
|
||||
lastGenerator = nextGenerator;
|
||||
}
|
||||
}
|
||||
return cachedGenerators.get(degree);
|
||||
}
|
||||
|
||||
public void encode(int[] toEncode, int ecBytes) {
|
||||
if (ecBytes == 0) {
|
||||
throw new IllegalArgumentException("No error correction bytes");
|
||||
}
|
||||
int dataBytes = toEncode.length - ecBytes;
|
||||
if (dataBytes <= 0) {
|
||||
throw new IllegalArgumentException("No data bytes provided");
|
||||
}
|
||||
GenericGFPoly generator = buildGenerator(ecBytes);
|
||||
int[] infoCoefficients = new int[dataBytes];
|
||||
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
|
||||
GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
|
||||
info = info.multiplyByMonomial(ecBytes, 1);
|
||||
GenericGFPoly remainder = info.divide(generator)[1];
|
||||
int[] coefficients = remainder.getCoefficients();
|
||||
int numZeroCoefficients = ecBytes - coefficients.length;
|
||||
for (int i = 0; i < numZeroCoefficients; i++) {
|
||||
toEncode[dataBytes + i] = 0;
|
||||
}
|
||||
System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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 Sean Owen
|
||||
*/
|
||||
public final class ReedSolomonException extends Exception {
|
||||
|
||||
public ReedSolomonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi;
|
||||
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class attempts to decode a barcode from an image, not by scanning the whole image,
|
||||
* but by scanning subsets of the image. This is important when there may be multiple barcodes in
|
||||
* an image, and detecting a barcode may find parts of multiple barcode and fail to decode
|
||||
* (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
|
||||
* 'quadrant' to cover the case where a barcode is found in the center.
|
||||
*
|
||||
* @see GenericMultipleBarcodeReader
|
||||
*/
|
||||
public final class ByQuadrantReader implements Reader {
|
||||
|
||||
private final Reader delegate;
|
||||
|
||||
public ByQuadrantReader(Reader delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image)
|
||||
throws NotFoundException, ChecksumException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, ChecksumException, FormatException {
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int halfWidth = width / 2;
|
||||
int halfHeight = height / 2;
|
||||
|
||||
try {
|
||||
// No need to call makeAbsolute as results will be relative to original top left here
|
||||
return delegate.decode(image.crop(0, 0, halfWidth, halfHeight), hints);
|
||||
} catch (NotFoundException re) {
|
||||
// continue
|
||||
}
|
||||
|
||||
try {
|
||||
Result result = delegate.decode(image.crop(halfWidth, 0, halfWidth, halfHeight), hints);
|
||||
makeAbsolute(result.getResultPoints(), halfWidth, 0);
|
||||
return result;
|
||||
} catch (NotFoundException re) {
|
||||
// continue
|
||||
}
|
||||
|
||||
try {
|
||||
Result result = delegate.decode(image.crop(0, halfHeight, halfWidth, halfHeight), hints);
|
||||
makeAbsolute(result.getResultPoints(), 0, halfHeight);
|
||||
return result;
|
||||
} catch (NotFoundException re) {
|
||||
// continue
|
||||
}
|
||||
|
||||
try {
|
||||
Result result = delegate.decode(image.crop(halfWidth, halfHeight, halfWidth, halfHeight), hints);
|
||||
makeAbsolute(result.getResultPoints(), halfWidth, halfHeight);
|
||||
return result;
|
||||
} catch (NotFoundException re) {
|
||||
// continue
|
||||
}
|
||||
|
||||
int quarterWidth = halfWidth / 2;
|
||||
int quarterHeight = halfHeight / 2;
|
||||
BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
|
||||
Result result = delegate.decode(center, hints);
|
||||
makeAbsolute(result.getResultPoints(), quarterWidth, quarterHeight);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
delegate.reset();
|
||||
}
|
||||
|
||||
private static void makeAbsolute(ResultPoint[] points, int leftOffset, int topOffset) {
|
||||
if (points != null) {
|
||||
for (int i = 0; i < points.length; i++) {
|
||||
ResultPoint relative = points[i];
|
||||
if (relative != null) {
|
||||
points[i] = new ResultPoint(relative.getX() + leftOffset, relative.getY() + topOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi;
|
||||
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
|
||||
* After one barcode is found, the areas left, above, right and below the barcode's
|
||||
* {@link ResultPoint}s are scanned, recursively.</p>
|
||||
*
|
||||
* <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
|
||||
* 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
|
||||
* detecting any one of them.</p>
|
||||
*
|
||||
* <p>That is, instead of passing a {@link Reader} a caller might pass
|
||||
* {@code new ByQuadrantReader(reader)}.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader {
|
||||
|
||||
private static final int MIN_DIMENSION_TO_RECUR = 100;
|
||||
private static final int MAX_DEPTH = 4;
|
||||
|
||||
static final Result[] EMPTY_RESULT_ARRAY = new Result[0];
|
||||
|
||||
private final Reader delegate;
|
||||
|
||||
public GenericMultipleBarcodeReader(Reader delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
|
||||
return decodeMultiple(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException {
|
||||
List<Result> results = new ArrayList<>();
|
||||
doDecodeMultiple(image, hints, results, 0, 0, 0);
|
||||
if (results.isEmpty()) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
return results.toArray(EMPTY_RESULT_ARRAY);
|
||||
}
|
||||
|
||||
private void doDecodeMultiple(BinaryBitmap image,
|
||||
Map<DecodeHintType,?> hints,
|
||||
List<Result> results,
|
||||
int xOffset,
|
||||
int yOffset,
|
||||
int currentDepth) {
|
||||
if (currentDepth > MAX_DEPTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
Result result;
|
||||
try {
|
||||
result = delegate.decode(image, hints);
|
||||
} catch (ReaderException ignored) {
|
||||
return;
|
||||
}
|
||||
boolean alreadyFound = false;
|
||||
for (Result existingResult : results) {
|
||||
if (existingResult.getText().equals(result.getText())) {
|
||||
alreadyFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!alreadyFound) {
|
||||
results.add(translateResultPoints(result, xOffset, yOffset));
|
||||
}
|
||||
ResultPoint[] resultPoints = result.getResultPoints();
|
||||
if (resultPoints == null || resultPoints.length == 0) {
|
||||
return;
|
||||
}
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
float minX = width;
|
||||
float minY = height;
|
||||
float maxX = 0.0f;
|
||||
float maxY = 0.0f;
|
||||
for (ResultPoint point : resultPoints) {
|
||||
if (point == null) {
|
||||
continue;
|
||||
}
|
||||
float x = point.getX();
|
||||
float y = point.getY();
|
||||
if (x < minX) {
|
||||
minX = x;
|
||||
}
|
||||
if (y < minY) {
|
||||
minY = y;
|
||||
}
|
||||
if (x > maxX) {
|
||||
maxX = x;
|
||||
}
|
||||
if (y > maxY) {
|
||||
maxY = y;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode left of barcode
|
||||
if (minX > MIN_DIMENSION_TO_RECUR) {
|
||||
doDecodeMultiple(image.crop(0, 0, (int) minX, height),
|
||||
hints, results,
|
||||
xOffset, yOffset,
|
||||
currentDepth + 1);
|
||||
}
|
||||
// Decode above barcode
|
||||
if (minY > MIN_DIMENSION_TO_RECUR) {
|
||||
doDecodeMultiple(image.crop(0, 0, width, (int) minY),
|
||||
hints, results,
|
||||
xOffset, yOffset,
|
||||
currentDepth + 1);
|
||||
}
|
||||
// Decode right of barcode
|
||||
if (maxX < width - MIN_DIMENSION_TO_RECUR) {
|
||||
doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height),
|
||||
hints, results,
|
||||
xOffset + (int) maxX, yOffset,
|
||||
currentDepth + 1);
|
||||
}
|
||||
// Decode below barcode
|
||||
if (maxY < height - MIN_DIMENSION_TO_RECUR) {
|
||||
doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY),
|
||||
hints, results,
|
||||
xOffset, yOffset + (int) maxY,
|
||||
currentDepth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static Result translateResultPoints(Result result, int xOffset, int yOffset) {
|
||||
ResultPoint[] oldResultPoints = result.getResultPoints();
|
||||
if (oldResultPoints == null) {
|
||||
return result;
|
||||
}
|
||||
ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length];
|
||||
for (int i = 0; i < oldResultPoints.length; i++) {
|
||||
ResultPoint oldPoint = oldResultPoints[i];
|
||||
if (oldPoint != null) {
|
||||
newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset);
|
||||
}
|
||||
}
|
||||
Result newResult = new Result(result.getText(),
|
||||
result.getRawBytes(),
|
||||
result.getNumBits(),
|
||||
newResultPoints,
|
||||
result.getBarcodeFormat(),
|
||||
result.getTimestamp());
|
||||
newResult.putAllMetadata(result.getResultMetadata());
|
||||
return newResult;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi;
|
||||
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementation of this interface attempt to read several barcodes from one image.
|
||||
*
|
||||
* @see com.google.zxing.Reader
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public interface MultipleBarcodeReader {
|
||||
|
||||
Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;
|
||||
|
||||
Result[] decodeMultiple(BinaryBitmap image,
|
||||
Map<DecodeHintType,?> hints) throws NotFoundException;
|
||||
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi.qrcode;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultMetadataType;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
import com.google.zxing.multi.MultipleBarcodeReader;
|
||||
import com.google.zxing.multi.qrcode.detector.MultiDetector;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode multiple QR Codes in an image.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
public final class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader {
|
||||
|
||||
private static final Result[] EMPTY_RESULT_ARRAY = new Result[0];
|
||||
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
|
||||
@Override
|
||||
public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
|
||||
return decodeMultiple(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {
|
||||
List<Result> results = new ArrayList<>();
|
||||
DetectorResult[] detectorResults = new MultiDetector(image.getBlackMatrix()).detectMulti(hints);
|
||||
for (DetectorResult detectorResult : detectorResults) {
|
||||
try {
|
||||
DecoderResult decoderResult = getDecoder().decode(detectorResult.getBits(), hints);
|
||||
ResultPoint[] points = detectorResult.getPoints();
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
|
||||
((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
|
||||
}
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,
|
||||
BarcodeFormat.QR_CODE);
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
|
||||
}
|
||||
String ecLevel = decoderResult.getECLevel();
|
||||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
if (decoderResult.hasStructuredAppend()) {
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
|
||||
decoderResult.getStructuredAppendSequenceNumber());
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
|
||||
decoderResult.getStructuredAppendParity());
|
||||
}
|
||||
results.add(result);
|
||||
} catch (ReaderException re) {
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
if (results.isEmpty()) {
|
||||
return EMPTY_RESULT_ARRAY;
|
||||
} else {
|
||||
results = processStructuredAppend(results);
|
||||
return results.toArray(EMPTY_RESULT_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Result> processStructuredAppend(List<Result> results) {
|
||||
List<Result> newResults = new ArrayList<>();
|
||||
List<Result> saResults = new ArrayList<>();
|
||||
for (Result result : results) {
|
||||
if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
|
||||
saResults.add(result);
|
||||
} else {
|
||||
newResults.add(result);
|
||||
}
|
||||
}
|
||||
if (saResults.isEmpty()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// sort and concatenate the SA list items
|
||||
Collections.sort(saResults, new SAComparator());
|
||||
StringBuilder newText = new StringBuilder();
|
||||
ByteArrayOutputStream newRawBytes = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream newByteSegment = new ByteArrayOutputStream();
|
||||
for (Result saResult : saResults) {
|
||||
newText.append(saResult.getText());
|
||||
byte[] saBytes = saResult.getRawBytes();
|
||||
newRawBytes.write(saBytes, 0, saBytes.length);
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterable<byte[]> byteSegments =
|
||||
(Iterable<byte[]>) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS);
|
||||
if (byteSegments != null) {
|
||||
for (byte[] segment : byteSegments) {
|
||||
newByteSegment.write(segment, 0, segment.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result newResult = new Result(newText.toString(), newRawBytes.toByteArray(), NO_POINTS, BarcodeFormat.QR_CODE);
|
||||
if (newByteSegment.size() > 0) {
|
||||
newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, Collections.singletonList(newByteSegment.toByteArray()));
|
||||
}
|
||||
newResults.add(newResult);
|
||||
return newResults;
|
||||
}
|
||||
|
||||
private static final class SAComparator implements Comparator<Result>, Serializable {
|
||||
@Override
|
||||
public int compare(Result a, Result b) {
|
||||
int aNumber = (int) a.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
|
||||
int bNumber = (int) b.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
|
||||
return Integer.compare(aNumber, bNumber);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi.qrcode.detector;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
import com.google.zxing.qrcode.detector.Detector;
|
||||
import com.google.zxing.qrcode.detector.FinderPatternInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
public final class MultiDetector extends Detector {
|
||||
|
||||
private static final DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0];
|
||||
|
||||
public MultiDetector(BitMatrix image) {
|
||||
super(image);
|
||||
}
|
||||
|
||||
public DetectorResult[] detectMulti(Map<DecodeHintType,?> hints) throws NotFoundException {
|
||||
BitMatrix image = getImage();
|
||||
ResultPointCallback resultPointCallback =
|
||||
hints == null ? null : (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
|
||||
MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image, resultPointCallback);
|
||||
FinderPatternInfo[] infos = finder.findMulti(hints);
|
||||
|
||||
if (infos.length == 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
List<DetectorResult> result = new ArrayList<>();
|
||||
for (FinderPatternInfo info : infos) {
|
||||
try {
|
||||
result.add(processFinderPatternInfo(info));
|
||||
} catch (ReaderException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
return EMPTY_DETECTOR_RESULTS;
|
||||
} else {
|
||||
return result.toArray(EMPTY_DETECTOR_RESULTS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.multi.qrcode.detector;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.detector.FinderPattern;
|
||||
import com.google.zxing.qrcode.detector.FinderPatternFinder;
|
||||
import com.google.zxing.qrcode.detector.FinderPatternInfo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
||||
* markers at three corners of a QR Code.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
|
||||
*
|
||||
* <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
|
||||
* QR code locations in the image.</p>
|
||||
*
|
||||
* <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
final class MultiFinderPatternFinder extends FinderPatternFinder {
|
||||
|
||||
private static final FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0];
|
||||
private static final FinderPattern[] EMPTY_FP_ARRAY = new FinderPattern[0];
|
||||
private static final FinderPattern[][] EMPTY_FP_2D_ARRAY = new FinderPattern[0][];
|
||||
|
||||
// TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
|
||||
// since it limits the number of regions to decode
|
||||
|
||||
// max. legal count of modules per QR code edge (177)
|
||||
private static final float MAX_MODULE_COUNT_PER_EDGE = 180;
|
||||
// min. legal count per modules per QR code edge (11)
|
||||
private static final float MIN_MODULE_COUNT_PER_EDGE = 9;
|
||||
|
||||
/**
|
||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
|
||||
* estimated modules sizes.
|
||||
*/
|
||||
private static final float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
|
||||
|
||||
/**
|
||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
|
||||
* estimated modules sizes.
|
||||
*/
|
||||
private static final float DIFF_MODSIZE_CUTOFF = 0.5f;
|
||||
|
||||
|
||||
/**
|
||||
* A comparator that orders FinderPatterns by their estimated module size.
|
||||
*/
|
||||
private static final class ModuleSizeComparator implements Comparator<FinderPattern>, Serializable {
|
||||
@Override
|
||||
public int compare(FinderPattern center1, FinderPattern center2) {
|
||||
float value = center2.getEstimatedModuleSize() - center1.getEstimatedModuleSize();
|
||||
return value < 0.0 ? -1 : value > 0.0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
|
||||
super(image, resultPointCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
||||
* those that have been detected at least 2 times, and whose module
|
||||
* size differs from the average among those patterns the least
|
||||
* @throws NotFoundException if 3 such finder patterns do not exist
|
||||
*/
|
||||
private FinderPattern[][] selectMultipleBestPatterns() throws NotFoundException {
|
||||
List<FinderPattern> possibleCenters = getPossibleCenters();
|
||||
int size = possibleCenters.size();
|
||||
|
||||
if (size < 3) {
|
||||
// Couldn't find enough finder patterns
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin HE modifications to safely detect multiple codes of equal size
|
||||
*/
|
||||
if (size == 3) {
|
||||
return new FinderPattern[][] { possibleCenters.toArray(EMPTY_FP_ARRAY) };
|
||||
}
|
||||
|
||||
// Sort by estimated module size to speed up the upcoming checks
|
||||
Collections.sort(possibleCenters, new ModuleSizeComparator());
|
||||
|
||||
/*
|
||||
* Now lets start: build a list of tuples of three finder locations that
|
||||
* - feature similar module sizes
|
||||
* - are placed in a distance so the estimated module count is within the QR specification
|
||||
* - have similar distance between upper left/right and left top/bottom finder patterns
|
||||
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
|
||||
* with pythagoras)
|
||||
*
|
||||
* Note: we allow each point to be used for more than one code region: this might seem
|
||||
* counterintuitive at first, but the performance penalty is not that big. At this point,
|
||||
* we cannot make a good quality decision whether the three finders actually represent
|
||||
* a QR code, or are just by chance laid out so it looks like there might be a QR code there.
|
||||
* So, if the layout seems right, lets have the decoder try to decode.
|
||||
*/
|
||||
|
||||
List<FinderPattern[]> results = new ArrayList<>(); // holder for the results
|
||||
|
||||
for (int i1 = 0; i1 < (size - 2); i1++) {
|
||||
FinderPattern p1 = possibleCenters.get(i1);
|
||||
if (p1 == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
|
||||
FinderPattern p2 = possibleCenters.get(i2);
|
||||
if (p2 == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare the expected module sizes; if they are really off, skip
|
||||
float vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()) /
|
||||
Math.min(p1.getEstimatedModuleSize(), p2.getEstimatedModuleSize());
|
||||
float vModSize12A = Math.abs(p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize());
|
||||
if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
|
||||
// break, since elements are ordered by the module size deviation there cannot be
|
||||
// any more interesting elements for the given p1.
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i3 = i2 + 1; i3 < size; i3++) {
|
||||
FinderPattern p3 = possibleCenters.get(i3);
|
||||
if (p3 == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare the expected module sizes; if they are really off, skip
|
||||
float vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()) /
|
||||
Math.min(p2.getEstimatedModuleSize(), p3.getEstimatedModuleSize());
|
||||
float vModSize23A = Math.abs(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize());
|
||||
if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
|
||||
// break, since elements are ordered by the module size deviation there cannot be
|
||||
// any more interesting elements for the given p1.
|
||||
break;
|
||||
}
|
||||
|
||||
FinderPattern[] test = {p1, p2, p3};
|
||||
ResultPoint.orderBestPatterns(test);
|
||||
|
||||
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
|
||||
FinderPatternInfo info = new FinderPatternInfo(test);
|
||||
float dA = ResultPoint.distance(info.getTopLeft(), info.getBottomLeft());
|
||||
float dC = ResultPoint.distance(info.getTopRight(), info.getBottomLeft());
|
||||
float dB = ResultPoint.distance(info.getTopLeft(), info.getTopRight());
|
||||
|
||||
// Check the sizes
|
||||
float estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0f);
|
||||
if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE ||
|
||||
estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the difference of the edge lengths in percent
|
||||
float vABBC = Math.abs((dA - dB) / Math.min(dA, dB));
|
||||
if (vABBC >= 0.1f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the diagonal length by assuming a 90° angle at topleft
|
||||
float dCpy = (float) Math.sqrt((double) dA * dA + (double) dB * dB);
|
||||
// Compare to the real distance in %
|
||||
float vPyC = Math.abs((dC - dCpy) / Math.min(dC, dCpy));
|
||||
|
||||
if (vPyC >= 0.1f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// All tests passed!
|
||||
results.add(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!results.isEmpty()) {
|
||||
return results.toArray(EMPTY_FP_2D_ARRAY);
|
||||
}
|
||||
|
||||
// Nothing found!
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
public FinderPatternInfo[] findMulti(Map<DecodeHintType,?> hints) throws NotFoundException {
|
||||
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||
BitMatrix image = getImage();
|
||||
int maxI = image.getHeight();
|
||||
int maxJ = image.getWidth();
|
||||
// We are looking for black/white/black/white/black modules in
|
||||
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
||||
|
||||
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||
// QR versions regardless of how dense they are.
|
||||
int iSkip = (3 * maxI) / (4 * MAX_MODULES);
|
||||
if (iSkip < MIN_SKIP || tryHarder) {
|
||||
iSkip = MIN_SKIP;
|
||||
}
|
||||
|
||||
int[] stateCount = new int[5];
|
||||
for (int i = iSkip - 1; i < maxI; i += iSkip) {
|
||||
// Get a row of black/white values
|
||||
clearCounts(stateCount);
|
||||
int currentState = 0;
|
||||
for (int j = 0; j < maxJ; j++) {
|
||||
if (image.get(j, i)) {
|
||||
// Black pixel
|
||||
if ((currentState & 1) == 1) { // Counting white pixels
|
||||
currentState++;
|
||||
}
|
||||
stateCount[currentState]++;
|
||||
} else { // White pixel
|
||||
if ((currentState & 1) == 0) { // Counting black pixels
|
||||
if (currentState == 4) { // A winner?
|
||||
if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes
|
||||
// Clear state to start looking again
|
||||
currentState = 0;
|
||||
clearCounts(stateCount);
|
||||
} else { // No, shift counts back by two
|
||||
shiftCounts2(stateCount);
|
||||
currentState = 3;
|
||||
}
|
||||
} else {
|
||||
stateCount[++currentState]++;
|
||||
}
|
||||
} else { // Counting white pixels
|
||||
stateCount[currentState]++;
|
||||
}
|
||||
}
|
||||
} // for j=...
|
||||
|
||||
if (foundPatternCross(stateCount)) {
|
||||
handlePossibleCenter(stateCount, i, maxJ);
|
||||
}
|
||||
} // for i=iSkip-1 ...
|
||||
FinderPattern[][] patternInfo = selectMultipleBestPatterns();
|
||||
List<FinderPatternInfo> result = new ArrayList<>();
|
||||
for (FinderPattern[] pattern : patternInfo) {
|
||||
ResultPoint.orderBestPatterns(pattern);
|
||||
result.add(new FinderPatternInfo(pattern));
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return EMPTY_RESULT_ARRAY;
|
||||
} else {
|
||||
return result.toArray(EMPTY_RESULT_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultMetadataType;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
import com.google.zxing.qrcode.decoder.Decoder;
|
||||
import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
|
||||
import com.google.zxing.qrcode.detector.Detector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode QR Codes in an image.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public class QRCodeReader implements Reader {
|
||||
|
||||
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
|
||||
private final Decoder decoder = new Decoder();
|
||||
|
||||
protected final Decoder getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and decodes a QR code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the QR code
|
||||
* @throws NotFoundException if a QR code cannot be found
|
||||
* @throws FormatException if a QR code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, ChecksumException, FormatException {
|
||||
DecoderResult decoderResult;
|
||||
ResultPoint[] points;
|
||||
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
|
||||
BitMatrix bits = extractPureBits(image.getBlackMatrix());
|
||||
decoderResult = decoder.decode(bits, hints);
|
||||
points = NO_POINTS;
|
||||
} else {
|
||||
DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);
|
||||
decoderResult = decoder.decode(detectorResult.getBits(), hints);
|
||||
points = detectorResult.getPoints();
|
||||
}
|
||||
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
|
||||
((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
|
||||
}
|
||||
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
|
||||
}
|
||||
String ecLevel = decoderResult.getECLevel();
|
||||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
if (decoderResult.hasStructuredAppend()) {
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
|
||||
decoderResult.getStructuredAppendSequenceNumber());
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
|
||||
decoderResult.getStructuredAppendParity());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {
|
||||
|
||||
int[] leftTopBlack = image.getTopLeftOnBit();
|
||||
int[] rightBottomBlack = image.getBottomRightOnBit();
|
||||
if (leftTopBlack == null || rightBottomBlack == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
float moduleSize = moduleSize(leftTopBlack, image);
|
||||
|
||||
int top = leftTopBlack[1];
|
||||
int bottom = rightBottomBlack[1];
|
||||
int left = leftTopBlack[0];
|
||||
int right = rightBottomBlack[0];
|
||||
|
||||
// Sanity check!
|
||||
if (left >= right || top >= bottom) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
if (bottom - top != right - left) {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
right = left + (bottom - top);
|
||||
if (right >= image.getWidth()) {
|
||||
// Abort if that would not make sense -- off image
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
int matrixWidth = Math.round((right - left + 1) / moduleSize);
|
||||
int matrixHeight = Math.round((bottom - top + 1) / moduleSize);
|
||||
if (matrixWidth <= 0 || matrixHeight <= 0) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
if (matrixHeight != matrixWidth) {
|
||||
// Only possibly decode square regions
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
int nudge = (int) (moduleSize / 2.0f);
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
int nudgedTooFarRight = left + (int) ((matrixWidth - 1) * moduleSize) - right;
|
||||
if (nudgedTooFarRight > 0) {
|
||||
if (nudgedTooFarRight > nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
left -= nudgedTooFarRight;
|
||||
}
|
||||
// See logic above
|
||||
int nudgedTooFarDown = top + (int) ((matrixHeight - 1) * moduleSize) - bottom;
|
||||
if (nudgedTooFarDown > 0) {
|
||||
if (nudgedTooFarDown > nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
top -= nudgedTooFarDown;
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight, 1);
|
||||
for (int y = 0; y < matrixHeight; y++) {
|
||||
int iOffset = top + (int) (y * moduleSize);
|
||||
for (int x = 0; x < matrixWidth; x++) {
|
||||
if (image.get(left + (int) (x * moduleSize), iOffset)) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static float moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
int x = leftTopBlack[0];
|
||||
int y = leftTopBlack[1];
|
||||
boolean inBlack = true;
|
||||
int transitions = 0;
|
||||
while (x < width && y < height) {
|
||||
if (inBlack != image.get(x, y)) {
|
||||
if (++transitions == 5) {
|
||||
break;
|
||||
}
|
||||
inBlack = !inBlack;
|
||||
}
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
if (x == width || y == height) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
return (x - leftTopBlack[0]) / 7.0f;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.qrcode.encoder.ByteMatrix;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.google.zxing.qrcode.encoder.Encoder;
|
||||
import com.google.zxing.qrcode.encoder.QRCode;
|
||||
|
||||
import org.telegram.messenger.R;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class QRCodeWriter {
|
||||
|
||||
private static final int QUIET_ZONE_SIZE = 4;
|
||||
private ByteMatrix input;
|
||||
private float[] radii = new float[8];
|
||||
private int imageBloks;
|
||||
private int imageBlockX;
|
||||
private int sideQuadSize;
|
||||
|
||||
public Bitmap encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints, Bitmap bitmap, Context context) throws WriterException {
|
||||
|
||||
if (contents.isEmpty()) {
|
||||
throw new IllegalArgumentException("Found empty contents");
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' + height);
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
|
||||
int quietZone = QUIET_ZONE_SIZE;
|
||||
if (hints != null) {
|
||||
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
|
||||
}
|
||||
if (hints.containsKey(EncodeHintType.MARGIN)) {
|
||||
quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
|
||||
}
|
||||
}
|
||||
|
||||
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
|
||||
|
||||
input = code.getMatrix();
|
||||
if (input == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int inputWidth = input.getWidth();
|
||||
int inputHeight = input.getHeight();
|
||||
|
||||
for (int x = 0; x < inputWidth; x++) {
|
||||
if (has(x, 0)) {
|
||||
sideQuadSize++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int qrWidth = inputWidth + (quietZone * 2);
|
||||
int qrHeight = inputHeight + (quietZone * 2);
|
||||
int outputWidth = Math.max(width, qrWidth);
|
||||
int outputHeight = Math.max(height, qrHeight);
|
||||
|
||||
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
|
||||
|
||||
int padding = 16;
|
||||
|
||||
int size = multiple * inputWidth + padding * 2;
|
||||
if (bitmap == null || bitmap.getWidth() != size) {
|
||||
bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(0xffffffff);
|
||||
Paint blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
blackPaint.setColor(0xff000000);
|
||||
|
||||
GradientDrawable rect = new GradientDrawable();
|
||||
rect.setShape(GradientDrawable.RECTANGLE);
|
||||
rect.setCornerRadii(radii);
|
||||
|
||||
imageBloks = Math.round((size - 32) / 4.65f / multiple);
|
||||
if (imageBloks % 2 != inputWidth % 2) {
|
||||
imageBloks++;
|
||||
}
|
||||
imageBlockX = (inputWidth - imageBloks) / 2;
|
||||
int imageSize = imageBloks * multiple;
|
||||
int imageX = (size - imageSize) / 2;
|
||||
|
||||
for (int a = 0; a < 3; a++) {
|
||||
int x, y;
|
||||
if (a == 0) {
|
||||
x = padding;
|
||||
y = padding;
|
||||
} else if (a == 1) {
|
||||
x = size - sideQuadSize * multiple - padding;
|
||||
y = padding;
|
||||
} else {
|
||||
x = padding;
|
||||
y = size - sideQuadSize * multiple - padding;
|
||||
}
|
||||
|
||||
float r = (sideQuadSize * multiple) / 3.0f;
|
||||
Arrays.fill(radii, r);
|
||||
|
||||
rect.setColor(0xff000000);
|
||||
rect.setBounds(x, y, x + sideQuadSize * multiple, y + sideQuadSize * multiple);
|
||||
rect.draw(canvas);
|
||||
|
||||
canvas.drawRect(x + multiple, y + multiple, x + (sideQuadSize - 1) * multiple, y + (sideQuadSize - 1) * multiple, blackPaint);
|
||||
|
||||
r = (sideQuadSize * multiple) / 4.0f;
|
||||
Arrays.fill(radii, r);
|
||||
rect.setColor(0xffffffff);
|
||||
rect.setBounds(x + multiple, y + multiple, x + (sideQuadSize - 1) * multiple, y + (sideQuadSize - 1) * multiple);
|
||||
rect.draw(canvas);
|
||||
|
||||
r = ((sideQuadSize - 2) * multiple) / 4.0f;
|
||||
Arrays.fill(radii, r);
|
||||
rect.setColor(0xff000000);
|
||||
rect.setBounds(x + multiple * 2, y + multiple * 2, x + (sideQuadSize - 2) * multiple, y + (sideQuadSize - 2) * multiple);
|
||||
rect.draw(canvas);
|
||||
}
|
||||
float r = multiple / 2.0f;
|
||||
|
||||
for (int y = 0, outputY = padding; y < inputHeight; y++, outputY += multiple) {
|
||||
for (int x = 0, outputX = padding; x < inputWidth; x++, outputX += multiple) {
|
||||
if (has(x, y)) {
|
||||
Arrays.fill(radii, r);
|
||||
if (has(x, y - 1)) {
|
||||
radii[0] = radii[1] = 0;
|
||||
radii[2] = radii[3] = 0;
|
||||
}
|
||||
if (has(x, y + 1)) {
|
||||
radii[6] = radii[7] = 0;
|
||||
radii[4] = radii[5] = 0;
|
||||
}
|
||||
if (has(x - 1, y)) {
|
||||
radii[0] = radii[1] = 0;
|
||||
radii[6] = radii[7] = 0;
|
||||
}
|
||||
if (has(x + 1, y)) {
|
||||
radii[2] = radii[3] = 0;
|
||||
radii[4] = radii[5] = 0;
|
||||
}
|
||||
rect.setColor(0xff000000);
|
||||
rect.setBounds(outputX, outputY, outputX + multiple, outputY + multiple);
|
||||
rect.draw(canvas);
|
||||
} else {
|
||||
boolean has = false;
|
||||
Arrays.fill(radii, 0);
|
||||
if (has(x - 1, y - 1) && has(x - 1, y) && has(x, y - 1)) {
|
||||
radii[0] = radii[1] = r;
|
||||
has = true;
|
||||
}
|
||||
if (has(x + 1, y - 1) && has(x + 1, y) && has(x, y - 1)) {
|
||||
radii[2] = radii[3] = r;
|
||||
has = true;
|
||||
}
|
||||
if (has(x - 1, y + 1) && has(x - 1, y) && has(x, y + 1)) {
|
||||
radii[6] = radii[7] = r;
|
||||
has = true;
|
||||
}
|
||||
if (has(x + 1, y + 1) && has(x + 1, y) && has(x, y + 1)) {
|
||||
radii[4] = radii[5] = r;
|
||||
has = true;
|
||||
}
|
||||
if (has) {
|
||||
canvas.drawRect(outputX, outputY, outputX + multiple, outputY + multiple, blackPaint);
|
||||
rect.setColor(0xffffffff);
|
||||
rect.setBounds(outputX, outputY, outputX + multiple, outputY + multiple);
|
||||
rect.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Drawable drawable = context.getResources().getDrawable(R.drawable.gem_l).mutate();
|
||||
drawable.setBounds(imageX, imageX, imageX + imageSize, imageX + imageSize);
|
||||
drawable.draw(canvas);
|
||||
|
||||
canvas.setBitmap(null);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private boolean has(int x, int y) {
|
||||
if (x >= imageBlockX && x < imageBlockX + imageBloks && y >= imageBlockX && y < imageBlockX + imageBloks) {
|
||||
return false;
|
||||
}
|
||||
if ((x < sideQuadSize || x >= input.getWidth() - sideQuadSize) && y < sideQuadSize) {
|
||||
return false;
|
||||
}
|
||||
if (x < sideQuadSize && y >= input.getHeight() - sideQuadSize) {
|
||||
return false;
|
||||
}
|
||||
return x >= 0 && y >= 0 && x < input.getWidth() && y < input.getHeight() && input.get(x, y) == 1;
|
||||
}
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class BitMatrixParser {
|
||||
|
||||
private final BitMatrix bitMatrix;
|
||||
private Version parsedVersion;
|
||||
private FormatInformation parsedFormatInfo;
|
||||
private boolean mirror;
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
* @throws FormatException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
|
||||
int dimension = bitMatrix.getHeight();
|
||||
if (dimension < 21 || (dimension & 0x03) != 1) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
this.bitMatrix = bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws FormatException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
FormatInformation readFormatInformation() throws FormatException {
|
||||
|
||||
if (parsedFormatInfo != null) {
|
||||
return parsedFormatInfo;
|
||||
}
|
||||
|
||||
// Read top-left format info bits
|
||||
int formatInfoBits1 = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
|
||||
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
|
||||
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for (int j = 5; j >= 0; j--) {
|
||||
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
|
||||
}
|
||||
|
||||
// Read the top-right/bottom-left pattern too
|
||||
int dimension = bitMatrix.getHeight();
|
||||
int formatInfoBits2 = 0;
|
||||
int jMin = dimension - 7;
|
||||
for (int j = dimension - 1; j >= jMin; j--) {
|
||||
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
|
||||
}
|
||||
for (int i = dimension - 8; i < dimension; i++) {
|
||||
formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
|
||||
}
|
||||
|
||||
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
|
||||
if (parsedFormatInfo != null) {
|
||||
return parsedFormatInfo;
|
||||
}
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws FormatException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
Version readVersion() throws FormatException {
|
||||
|
||||
if (parsedVersion != null) {
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
int dimension = bitMatrix.getHeight();
|
||||
|
||||
int provisionalVersion = (dimension - 17) / 4;
|
||||
if (provisionalVersion <= 6) {
|
||||
return Version.getVersionForNumber(provisionalVersion);
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
int versionBits = 0;
|
||||
int ijMin = dimension - 11;
|
||||
for (int j = 5; j >= 0; j--) {
|
||||
for (int i = dimension - 9; i >= ijMin; i--) {
|
||||
versionBits = copyBit(i, j, versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
Version theParsedVersion = Version.decodeVersionInformation(versionBits);
|
||||
if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) {
|
||||
parsedVersion = theParsedVersion;
|
||||
return theParsedVersion;
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
versionBits = 0;
|
||||
for (int i = 5; i >= 0; i--) {
|
||||
for (int j = dimension - 9; j >= ijMin; j--) {
|
||||
versionBits = copyBit(i, j, versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
theParsedVersion = Version.decodeVersionInformation(versionBits);
|
||||
if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) {
|
||||
parsedVersion = theParsedVersion;
|
||||
return theParsedVersion;
|
||||
}
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
private int copyBit(int i, int j, int versionBits) {
|
||||
boolean bit = mirror ? bitMatrix.get(j, i) : bitMatrix.get(i, j);
|
||||
return bit ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws FormatException if the exact number of bytes expected is not read
|
||||
*/
|
||||
byte[] readCodewords() throws FormatException {
|
||||
|
||||
FormatInformation formatInfo = readFormatInformation();
|
||||
Version version = readVersion();
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
DataMask dataMask = DataMask.values()[formatInfo.getDataMask()];
|
||||
int dimension = bitMatrix.getHeight();
|
||||
dataMask.unmaskBitMatrix(bitMatrix, dimension);
|
||||
|
||||
BitMatrix functionPattern = version.buildFunctionPattern();
|
||||
|
||||
boolean readingUp = true;
|
||||
byte[] result = new byte[version.getTotalCodewords()];
|
||||
int resultOffset = 0;
|
||||
int currentByte = 0;
|
||||
int bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for (int j = dimension - 1; j > 0; j -= 2) {
|
||||
if (j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for (int count = 0; count < dimension; count++) {
|
||||
int i = readingUp ? dimension - 1 - count : count;
|
||||
for (int col = 0; col < 2; col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!functionPattern.get(j - col, i)) {
|
||||
// Read a bit
|
||||
bitsRead++;
|
||||
currentByte <<= 1;
|
||||
if (bitMatrix.get(j - col, i)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if (bitsRead == 8) {
|
||||
result[resultOffset++] = (byte) currentByte;
|
||||
bitsRead = 0;
|
||||
currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if (resultOffset != version.getTotalCodewords()) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
void remask() {
|
||||
if (parsedFormatInfo == null) {
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
DataMask dataMask = DataMask.values()[parsedFormatInfo.getDataMask()];
|
||||
int dimension = bitMatrix.getHeight();
|
||||
dataMask.unmaskBitMatrix(bitMatrix, dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param mirror Whether to read version and format information mirrored.
|
||||
*/
|
||||
void setMirror(boolean mirror) {
|
||||
parsedVersion = null;
|
||||
parsedFormatInfo = null;
|
||||
this.mirror = mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
void mirror() {
|
||||
for (int x = 0; x < bitMatrix.getWidth(); x++) {
|
||||
for (int y = x + 1; y < bitMatrix.getHeight(); y++) {
|
||||
if (bitMatrix.get(x, y) != bitMatrix.get(y, x)) {
|
||||
bitMatrix.flip(y, x);
|
||||
bitMatrix.flip(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>When QR Codes use multiple data blocks, they are actually interleaved.
|
||||
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
* method will separate the data into original blocks.</p>
|
||||
*
|
||||
* @param rawCodewords bytes as read directly from the QR Code
|
||||
* @param version version of the QR Code
|
||||
* @param ecLevel error-correction level of the QR Code
|
||||
* @return DataBlocks containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
static DataBlock[] getDataBlocks(byte[] rawCodewords,
|
||||
Version version,
|
||||
ErrorCorrectionLevel ecLevel) {
|
||||
|
||||
if (rawCodewords.length != version.getTotalCodewords()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
|
||||
// First count the total number of data blocks
|
||||
int totalBlocks = 0;
|
||||
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||
for (Version.ECB ecBlock : ecBlockArray) {
|
||||
totalBlocks += ecBlock.getCount();
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
DataBlock[] result = new DataBlock[totalBlocks];
|
||||
int numResultBlocks = 0;
|
||||
for (Version.ECB ecBlock : ecBlockArray) {
|
||||
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||
int numDataCodewords = ecBlock.getDataCodewords();
|
||||
int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;
|
||||
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 more byte. Figure out where these start.
|
||||
int shorterBlocksTotalCodewords = result[0].codewords.length;
|
||||
int longerBlocksStartAt = result.length - 1;
|
||||
while (longerBlocksStartAt >= 0) {
|
||||
int numCodewords = result[longerBlocksStartAt].codewords.length;
|
||||
if (numCodewords == shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
longerBlocksStartAt--;
|
||||
}
|
||||
longerBlocksStartAt++;
|
||||
|
||||
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
int rawCodewordsOffset = 0;
|
||||
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
||||
for (int j = 0; j < numResultBlocks; j++) {
|
||||
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
|
||||
result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
int max = result[0].codewords.length;
|
||||
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
|
||||
for (int j = 0; j < numResultBlocks; j++) {
|
||||
int iOffset = j < longerBlocksStartAt ? i : i + 1;
|
||||
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int getNumDataCodewords() {
|
||||
return numDataCodewords;
|
||||
}
|
||||
|
||||
byte[] getCodewords() {
|
||||
return codewords;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
* of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
|
||||
* including areas used for finder patterns, timing patterns, etc. These areas should be unused
|
||||
* after the point they are unmasked anyway.</p>
|
||||
*
|
||||
* <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
* and j is row position. In fact, as the text says, i is row position and j is column position.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
enum DataMask {
|
||||
|
||||
// See ISO 18004:2006 6.8.1
|
||||
|
||||
/**
|
||||
* 000: mask bits for which (x + y) mod 2 == 0
|
||||
*/
|
||||
DATA_MASK_000() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return ((i + j) & 0x01) == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 001: mask bits for which x mod 2 == 0
|
||||
*/
|
||||
DATA_MASK_001() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return (i & 0x01) == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 010: mask bits for which y mod 3 == 0
|
||||
*/
|
||||
DATA_MASK_010() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return j % 3 == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 011: mask bits for which (x + y) mod 3 == 0
|
||||
*/
|
||||
DATA_MASK_011() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return (i + j) % 3 == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
||||
*/
|
||||
DATA_MASK_100() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return (((i / 2) + (j / 3)) & 0x01) == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
|
||||
* equivalently, such that xy mod 6 == 0
|
||||
*/
|
||||
DATA_MASK_101() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return (i * j) % 6 == 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
|
||||
* equivalently, such that xy mod 6 < 3
|
||||
*/
|
||||
DATA_MASK_110() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return ((i * j) % 6) < 3;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
|
||||
* equivalently, such that (x + y + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
DATA_MASK_111() {
|
||||
@Override
|
||||
boolean isMasked(int i, int j) {
|
||||
return ((i + j + ((i * j) % 3)) & 0x01) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// End of enum constants.
|
||||
|
||||
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param bits representation of QR Code bits
|
||||
* @param dimension dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
final void unmaskBitMatrix(BitMatrix bits, int dimension) {
|
||||
for (int i = 0; i < dimension; i++) {
|
||||
for (int j = 0; j < dimension; j++) {
|
||||
if (isMasked(i, j)) {
|
||||
bits.flip(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract boolean isMasked(int i, int j);
|
||||
|
||||
}
|
|
@ -1,360 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.common.BitSource;
|
||||
import com.google.zxing.common.CharacterSetECI;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
* in one QR Code. This class decodes the bits back into text.</p>
|
||||
*
|
||||
* <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DecodedBitStreamParser {
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.4 Table 5
|
||||
*/
|
||||
private static final char[] ALPHANUMERIC_CHARS =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".toCharArray();
|
||||
private static final int GB2312_SUBSET = 1;
|
||||
|
||||
private DecodedBitStreamParser() {
|
||||
}
|
||||
|
||||
static DecoderResult decode(byte[] bytes,
|
||||
Version version,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
Map<DecodeHintType,?> hints) throws FormatException {
|
||||
BitSource bits = new BitSource(bytes);
|
||||
StringBuilder result = new StringBuilder(50);
|
||||
List<byte[]> byteSegments = new ArrayList<>(1);
|
||||
int symbolSequence = -1;
|
||||
int parityData = -1;
|
||||
|
||||
try {
|
||||
CharacterSetECI currentCharacterSetECI = null;
|
||||
boolean fc1InEffect = false;
|
||||
Mode mode;
|
||||
do {
|
||||
// While still another segment to read...
|
||||
if (bits.available() < 4) {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
mode = Mode.TERMINATOR;
|
||||
} else {
|
||||
mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
switch (mode) {
|
||||
case TERMINATOR:
|
||||
break;
|
||||
case FNC1_FIRST_POSITION:
|
||||
case FNC1_SECOND_POSITION:
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
fc1InEffect = true;
|
||||
break;
|
||||
case STRUCTURED_APPEND:
|
||||
if (bits.available() < 16) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
symbolSequence = bits.readBits(8);
|
||||
parityData = bits.readBits(8);
|
||||
break;
|
||||
case ECI:
|
||||
// Count doesn't apply to ECI
|
||||
int value = parseECIValue(bits);
|
||||
currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
|
||||
if (currentCharacterSetECI == null) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
break;
|
||||
case HANZI:
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
// Chinese mode contains a sub set indicator right after mode indicator
|
||||
int subset = bits.readBits(4);
|
||||
int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
|
||||
if (subset == GB2312_SUBSET) {
|
||||
decodeHanziSegment(bits, result, countHanzi);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
int count = bits.readBits(mode.getCharacterCountBits(version));
|
||||
switch (mode) {
|
||||
case NUMERIC:
|
||||
decodeNumericSegment(bits, result, count);
|
||||
break;
|
||||
case ALPHANUMERIC:
|
||||
decodeAlphanumericSegment(bits, result, count, fc1InEffect);
|
||||
break;
|
||||
case BYTE:
|
||||
decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
|
||||
break;
|
||||
case KANJI:
|
||||
decodeKanjiSegment(bits, result, count);
|
||||
break;
|
||||
default:
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (mode != Mode.TERMINATOR);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// from readBits() calls
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
return new DecoderResult(bytes,
|
||||
result.toString(),
|
||||
byteSegments.isEmpty() ? null : byteSegments,
|
||||
ecLevel == null ? null : ecLevel.toString(),
|
||||
symbolSequence,
|
||||
parityData);
|
||||
}
|
||||
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
private static void decodeHanziSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count) throws FormatException {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (count * 13 > bits.available()) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
byte[] buffer = new byte[2 * count];
|
||||
int offset = 0;
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
int twoBytes = bits.readBits(13);
|
||||
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||
if (assembledTwoBytes < 0x00A00) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);
|
||||
buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
|
||||
try {
|
||||
result.append(new String(buffer, StringUtils.GB2312));
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private static void decodeKanjiSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count) throws FormatException {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (count * 13 > bits.available()) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
byte[] buffer = new byte[2 * count];
|
||||
int offset = 0;
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
int twoBytes = bits.readBits(13);
|
||||
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||
if (assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
buffer[offset] = (byte) (assembledTwoBytes >> 8);
|
||||
buffer[offset + 1] = (byte) assembledTwoBytes;
|
||||
offset += 2;
|
||||
count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
try {
|
||||
result.append(new String(buffer, StringUtils.SHIFT_JIS));
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private static void decodeByteSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count,
|
||||
CharacterSetECI currentCharacterSetECI,
|
||||
Collection<byte[]> byteSegments,
|
||||
Map<DecodeHintType,?> hints) throws FormatException {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (8 * count > bits.available()) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
byte[] readBytes = new byte[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
readBytes[i] = (byte) bits.readBits(8);
|
||||
}
|
||||
String encoding;
|
||||
if (currentCharacterSetECI == null) {
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
encoding = StringUtils.guessEncoding(readBytes, hints);
|
||||
} else {
|
||||
encoding = currentCharacterSetECI.name();
|
||||
}
|
||||
try {
|
||||
result.append(new String(readBytes, encoding));
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
byteSegments.add(readBytes);
|
||||
}
|
||||
|
||||
private static char toAlphaNumericChar(int value) throws FormatException {
|
||||
if (value >= ALPHANUMERIC_CHARS.length) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
return ALPHANUMERIC_CHARS[value];
|
||||
}
|
||||
|
||||
private static void decodeAlphanumericSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count,
|
||||
boolean fc1InEffect) throws FormatException {
|
||||
// Read two characters at a time
|
||||
int start = result.length();
|
||||
while (count > 1) {
|
||||
if (bits.available() < 11) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
int nextTwoCharsBits = bits.readBits(11);
|
||||
result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
|
||||
result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
|
||||
count -= 2;
|
||||
}
|
||||
if (count == 1) {
|
||||
// special case: one character left
|
||||
if (bits.available() < 6) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
result.append(toAlphaNumericChar(bits.readBits(6)));
|
||||
}
|
||||
// See section 6.4.8.1, 6.4.8.2
|
||||
if (fc1InEffect) {
|
||||
// We need to massage the result a bit if in an FNC1 mode:
|
||||
for (int i = start; i < result.length(); i++) {
|
||||
if (result.charAt(i) == '%') {
|
||||
if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
|
||||
// %% is rendered as %
|
||||
result.deleteCharAt(i + 1);
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
result.setCharAt(i, (char) 0x1D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void decodeNumericSegment(BitSource bits,
|
||||
StringBuilder result,
|
||||
int count) throws FormatException {
|
||||
// Read three digits at a time
|
||||
while (count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
if (bits.available() < 10) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
int threeDigitsBits = bits.readBits(10);
|
||||
if (threeDigitsBits >= 1000) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
result.append(toAlphaNumericChar(threeDigitsBits / 100));
|
||||
result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
|
||||
result.append(toAlphaNumericChar(threeDigitsBits % 10));
|
||||
count -= 3;
|
||||
}
|
||||
if (count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if (bits.available() < 7) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
int twoDigitsBits = bits.readBits(7);
|
||||
if (twoDigitsBits >= 100) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
result.append(toAlphaNumericChar(twoDigitsBits / 10));
|
||||
result.append(toAlphaNumericChar(twoDigitsBits % 10));
|
||||
} else if (count == 1) {
|
||||
// One digit left over to read
|
||||
if (bits.available() < 4) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
int digitBits = bits.readBits(4);
|
||||
if (digitBits >= 10) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
result.append(toAlphaNumericChar(digitBits));
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseECIValue(BitSource bits) throws FormatException {
|
||||
int firstByte = bits.readBits(8);
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return firstByte & 0x7F;
|
||||
}
|
||||
if ((firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
int secondByte = bits.readBits(8);
|
||||
return ((firstByte & 0x3F) << 8) | secondByte;
|
||||
}
|
||||
if ((firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
int secondThirdBytes = bits.readBits(16);
|
||||
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
|
||||
}
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
* the QR Code from an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class Decoder {
|
||||
|
||||
private final ReedSolomonDecoder rsDecoder;
|
||||
|
||||
public Decoder() {
|
||||
rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
|
||||
}
|
||||
|
||||
public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param image booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public DecoderResult decode(boolean[][] image, Map<DecodeHintType,?> hints)
|
||||
throws ChecksumException, FormatException {
|
||||
return decode(BitMatrix.parse(image), hints);
|
||||
}
|
||||
|
||||
public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {
|
||||
return decode(bits, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param bits booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints)
|
||||
throws FormatException, ChecksumException {
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||
FormatException fe = null;
|
||||
ChecksumException ce = null;
|
||||
try {
|
||||
return decode(parser, hints);
|
||||
} catch (FormatException e) {
|
||||
fe = e;
|
||||
} catch (ChecksumException e) {
|
||||
ce = e;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Revert the bit matrix
|
||||
parser.remask();
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
parser.setMirror(true);
|
||||
|
||||
// Preemptively read the version.
|
||||
parser.readVersion();
|
||||
|
||||
// Preemptively read the format information.
|
||||
parser.readFormatInformation();
|
||||
|
||||
/*
|
||||
* Since we're here, this means we have successfully detected some kind
|
||||
* of version and format information when mirrored. This is a good sign,
|
||||
* that the QR code may be mirrored, and we should try once more with a
|
||||
* mirrored content.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
parser.mirror();
|
||||
|
||||
DecoderResult result = decode(parser, hints);
|
||||
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
result.setOther(new QRCodeDecoderMetaData(true));
|
||||
|
||||
return result;
|
||||
|
||||
} catch (FormatException | ChecksumException e) {
|
||||
// Throw the exception from the original reading
|
||||
if (fe != null) {
|
||||
throw fe;
|
||||
}
|
||||
throw ce; // If fe is null, this can't be
|
||||
}
|
||||
}
|
||||
|
||||
private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)
|
||||
throws FormatException, ChecksumException {
|
||||
Version version = parser.readVersion();
|
||||
ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
|
||||
|
||||
// Read codewords
|
||||
byte[] codewords = parser.readCodewords();
|
||||
// Separate into data blocks
|
||||
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
|
||||
|
||||
// Count total number of data bytes
|
||||
int totalBytes = 0;
|
||||
for (DataBlock dataBlock : dataBlocks) {
|
||||
totalBytes += dataBlock.getNumDataCodewords();
|
||||
}
|
||||
byte[] resultBytes = new byte[totalBytes];
|
||||
int resultOffset = 0;
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
for (DataBlock dataBlock : dataBlocks) {
|
||||
byte[] codewordBytes = dataBlock.getCodewords();
|
||||
int numDataCodewords = dataBlock.getNumDataCodewords();
|
||||
correctErrors(codewordBytes, numDataCodewords);
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
resultBytes[resultOffset++] = codewordBytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {
|
||||
int numCodewords = codewordBytes.length;
|
||||
// First read into an array of ints
|
||||
int[] codewordsInts = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++) {
|
||||
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||
}
|
||||
try {
|
||||
rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords);
|
||||
} catch (ReedSolomonException ignored) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
codewordBytes[i] = (byte) codewordsInts[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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 Sean Owen
|
||||
*/
|
||||
public enum ErrorCorrectionLevel {
|
||||
|
||||
/** L = ~7% correction */
|
||||
L(0x01),
|
||||
/** M = ~15% correction */
|
||||
M(0x00),
|
||||
/** Q = ~25% correction */
|
||||
Q(0x03),
|
||||
/** H = ~30% correction */
|
||||
H(0x02);
|
||||
|
||||
private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
|
||||
|
||||
private final int bits;
|
||||
|
||||
ErrorCorrectionLevel(int bits) {
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits int containing the two bits encoding a QR Code's error correction level
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static ErrorCorrectionLevel forBits(int bits) {
|
||||
if (bits < 0 || bits >= FOR_BITS.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return FOR_BITS[bits];
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
* error correction level.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see DataMask
|
||||
* @see ErrorCorrectionLevel
|
||||
*/
|
||||
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 = {
|
||||
{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 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) {
|
||||
return Integer.bitCount(a ^ b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maskedFormatInfo1 format info indicator, with mask still applied
|
||||
* @param maskedFormatInfo2 second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
||||
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
|
||||
if (formatInfo != null) {
|
||||
return formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
|
||||
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
|
||||
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
int bestDifference = Integer.MAX_VALUE;
|
||||
int bestFormatInfo = 0;
|
||||
for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) {
|
||||
int targetInfo = decodeInfo[0];
|
||||
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation(decodeInfo[1]);
|
||||
}
|
||||
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
if (maskedFormatInfo1 != maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
|
||||
// differing means we found a match
|
||||
if (bestDifference <= 3) {
|
||||
return new FormatInformation(bestFormatInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel getErrorCorrectionLevel() {
|
||||
return errorCorrectionLevel;
|
||||
}
|
||||
|
||||
byte getDataMask() {
|
||||
return dataMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (errorCorrectionLevel.ordinal() << 3) | dataMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
FormatInformation other = (FormatInformation) o;
|
||||
return this.errorCorrectionLevel == other.errorCorrectionLevel &&
|
||||
this.dataMask == other.dataMask;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <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 Sean Owen
|
||||
*/
|
||||
public enum Mode {
|
||||
|
||||
TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode...
|
||||
NUMERIC(new int[]{10, 12, 14}, 0x01),
|
||||
ALPHANUMERIC(new int[]{9, 11, 13}, 0x02),
|
||||
STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported
|
||||
BYTE(new int[]{8, 16, 16}, 0x04),
|
||||
ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply
|
||||
KANJI(new int[]{8, 10, 12}, 0x08),
|
||||
FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05),
|
||||
FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09),
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
HANZI(new int[]{8, 10, 12}, 0x0D);
|
||||
|
||||
private final int[] characterCountBitsForVersions;
|
||||
private final int bits;
|
||||
|
||||
Mode(int[] characterCountBitsForVersions, int bits) {
|
||||
this.characterCountBitsForVersions = characterCountBitsForVersions;
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits four bits encoding a QR Code data mode
|
||||
* @return Mode encoded by these bits
|
||||
* @throws IllegalArgumentException if bits do not correspond to a known mode
|
||||
*/
|
||||
public static Mode forBits(int bits) {
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return TERMINATOR;
|
||||
case 0x1:
|
||||
return NUMERIC;
|
||||
case 0x2:
|
||||
return ALPHANUMERIC;
|
||||
case 0x3:
|
||||
return STRUCTURED_APPEND;
|
||||
case 0x4:
|
||||
return BYTE;
|
||||
case 0x5:
|
||||
return FNC1_FIRST_POSITION;
|
||||
case 0x7:
|
||||
return ECI;
|
||||
case 0x8:
|
||||
return KANJI;
|
||||
case 0x9:
|
||||
return FNC1_SECOND_POSITION;
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return HANZI;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
public 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];
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
/**
|
||||
* Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the
|
||||
* decoding caller. Callers are expected to process this.
|
||||
*
|
||||
* @see com.google.zxing.common.DecoderResult#getOther()
|
||||
*/
|
||||
public final class QRCodeDecoderMetaData {
|
||||
|
||||
private final boolean mirrored;
|
||||
|
||||
QRCodeDecoderMetaData(boolean mirrored) {
|
||||
this.mirrored = mirrored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the QR Code was mirrored.
|
||||
*/
|
||||
public boolean isMirrored() {
|
||||
return mirrored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the result points' order correction due to mirroring.
|
||||
*
|
||||
* @param points Array of points to apply mirror correction to.
|
||||
*/
|
||||
public void applyMirroredCorrection(ResultPoint[] points) {
|
||||
if (!mirrored || points == null || points.length < 3) {
|
||||
return;
|
||||
}
|
||||
ResultPoint bottomLeft = points[0];
|
||||
points[0] = points[2];
|
||||
points[2] = bottomLeft;
|
||||
// No need to 'fix' top-left and alignment pattern.
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue