(? ... !~

This commit is contained in:
世界 2020-03-26 21:40:56 +08:00
parent e68969e2b9
commit 8be981e4a0
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
327 changed files with 17862 additions and 21412 deletions

25
.gitattributes vendored Normal file
View File

@ -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

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
patreon: NekoWorkshop
patreon: NekoXDev

30
.github/workflows/canary.yml vendored Normal file
View File

@ -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 }}

77
.github/workflows/foss.yml vendored Normal file
View File

@ -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"

77
.github/workflows/release.yml vendored Normal file
View File

@ -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

42
.gitmodules vendored
View File

@ -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

View File

@ -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
View File

@ -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>.

View File

@ -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!

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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.

View File

@ -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"
}

View File

@ -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)

View File

@ -1,4 +1,4 @@
APP_PLATFORM := android-16
APP_PLATFORM := android-18
NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_static
APP_SHORT_COMMANDS := true

View File

@ -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

View File

@ -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

View File

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

View File

@ -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;
};
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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();
}
}

View File

@ -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 "";
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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,
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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,
}

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 + ')';
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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];
}
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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