mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-25 18:59:41 +01:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e6fa333bf | ||
|
|
e1a4c7a5ba | ||
|
|
f61d6c31a0 | ||
|
|
f111d35015 | ||
|
|
84e17bb70f | ||
|
|
eae3965ef9 | ||
|
|
92aaf88110 | ||
|
|
f4414aed91 | ||
|
|
52b60c5df1 | ||
|
|
b9fcf08391 | ||
|
|
83760527fb | ||
|
|
9091cd940a | ||
|
|
eb878643c1 | ||
|
|
37c85e9c97 | ||
|
|
f0cd40871b | ||
|
|
a237e88170 | ||
|
|
77a19ab692 | ||
|
|
e1e540783d | ||
|
|
31e244239a | ||
|
|
fd6cf30685 | ||
|
|
a3e2c24bc2 | ||
|
|
d8fbfe250a | ||
|
|
cf825d4972 | ||
|
|
2995252420 | ||
|
|
fb0ab92a85 | ||
|
|
8582a4bb93 | ||
|
|
830839072a | ||
|
|
ab61c11d26 | ||
|
|
2d21dade9b | ||
|
|
f17faf67eb | ||
|
|
e401519a6b | ||
|
|
877dcd6577 | ||
|
|
edae94bfa3 | ||
|
|
5a2ae3b313 | ||
|
|
ef262dc8bb | ||
|
|
e0e8d694d1 | ||
|
|
fd03016da2 | ||
|
|
bd2b313993 | ||
|
|
b89b770f4f | ||
|
|
100ac45f1c | ||
|
|
cd09c3f936 | ||
|
|
f998adfa8f |
@@ -1,7 +1,7 @@
|
||||
[general]
|
||||
project=jdk-updates
|
||||
jbs=JDK
|
||||
version=11.0.15
|
||||
version=11.0.16
|
||||
|
||||
[checks]
|
||||
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace
|
||||
|
||||
170
README.md
170
README.md
@@ -3,5 +3,171 @@
|
||||
# Welcome to JetBrains Runtime!
|
||||
<a name="jetbrains-runtime"></a>
|
||||
|
||||
Do not use this branch. It is [outdated](https://youtrack.jetbrains.com/issue/JBR-4375/New-branch-naming-policy-in-JBR-repo).
|
||||
Please use [jbr11](https://github.com/JetBrains/JetBrainsRuntime/tree/jbr11) instead.
|
||||
JetBrains Runtime is a fork of [OpenJDK](https://github.com/openjdk/jdk) available for Windows, Mac OS X, and Linux.
|
||||
It includes a number enhancements in font rendering, HiDPI support, ligatures, performance improvements, and bugfixes.
|
||||
|
||||
## Releases
|
||||
Download the latest releases of JetBrains Runtime to use with JetBrains IDEs. The full list
|
||||
can be found on the [releases page](https://github.com/JetBrains/JetBrainsRuntime/releases).
|
||||
|
||||
| IDE Version | Latest JBR | Date Released |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------|---------------|
|
||||
| 2022.1 | [11_0_15-b2043.56](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr11_0_15b2043.56) | 17-May-2022 |
|
||||
| 2021.3 | [11_0_14_1-b1751.46](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr11_0_14_1b1751.46) | 21-Feb-2022 |
|
||||
| 2021.2 | [11_0_13-b1504.49](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jb11_0_13-b1504.49) | 15-Nov-2021 |
|
||||
| 2021.1 | [11.0.11+9-b1341.60](https://confluence.jetbrains.com/pages/viewpage.action?pageId=218857477) | 15-Jun-2021 |
|
||||
| 2020.3 | [11_0_11-b1145.115](https://confluence.jetbrains.com/pages/viewpage.action?pageId=219349001) | 21-Jun-2021 |
|
||||
|
||||
## Contents
|
||||
- [Welcome to JetBrains Runtime](#jetbrains-runtime)
|
||||
- [Products Built on JetBrains Runtime](#products-built-on-jetbrains-runtime)
|
||||
- [Getting Sources](#getting-sources)
|
||||
- [macOS, Linux](#macos-linux)
|
||||
- [Windows](#sources-windows)
|
||||
- [Configuring the Build Environment](#configuring-the-build-environment)
|
||||
- [Linux (Docker)](#linux-docker)
|
||||
- [Ubuntu Linux](#ubuntu-linux)
|
||||
- [Windows](#build-windows)
|
||||
- [macOS](#macos)
|
||||
- [Developing](#developing)
|
||||
- [Contributing](#contributing)
|
||||
- [Resources](#resources)
|
||||
|
||||
## Products Built on JetBrains Runtime
|
||||
* [Android Studio](https://developer.android.com/studio). The official IDE for Google's Android operating system.
|
||||
* [CLion](https://www.jetbrains.com/clion/). A cross-platform IDE for C and C++ from JetBrains.
|
||||
* [DataGrip](https://www.jetbrains.com/datagrip/). The IDE for Databases and SQL from JetBrains.
|
||||
* [GoLand](https://www.jetbrains.com/go/). The cross-platform Go IDE from JetBrains.
|
||||
* [IntelliJ IDEA](https://www.jetbrains.com/idea/). The IDE for JVM from JetBrains.
|
||||
* [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html). The Java profiler.
|
||||
* [PhpStorm](https://www.jetbrains.com/phpstorm/). The PHP IDE from JetBrains.
|
||||
* [PyCharm](https://www.jetbrains.com/pycharm/). The Python IDE from JetBrains.
|
||||
* [Rider](https://www.jetbrains.com/rider/). The cross-platform .NET IDE from JetBrains.
|
||||
* [RubyMine](https://www.jetbrains.com/ruby/). The Ruby and Rails IDE from JetBrains.
|
||||
* [WebStorm](https://www.jetbrains.com/webstorm/). The JavaScript IDE from JetBrains.
|
||||
* [YourKit](https://www.yourkit.com/). Java and .NET profilers.
|
||||
|
||||
## Getting Sources
|
||||
### macOS, Linux
|
||||
```
|
||||
git config --global core.autocrlf input
|
||||
git clone git@github.com:JetBrains/JetBrainsRuntime.git
|
||||
```
|
||||
|
||||
### Windows
|
||||
<a name="sources-windows"></a>
|
||||
```
|
||||
git config --global core.autocrlf false
|
||||
git clone git@github.com:JetBrains/JetBrainsRuntime.git
|
||||
```
|
||||
|
||||
## Configuring the Build Environment
|
||||
Here are quick per-platform instructions for those who can't wait to get started.
|
||||
Please refer to [OpenJDK build docs](http://hg.openjdk.java.net/jdk/jdk11/raw-file/tip/doc/building.html) for in-depth
|
||||
coverage of all the details.
|
||||
|
||||
> **_TIP:_** To get a preliminary report of what's missing, run `./configure` and check its output.
|
||||
> It would usually have a meaningful advice on how to solve the problem.
|
||||
|
||||
### Linux (Docker)
|
||||
Create a container:
|
||||
```
|
||||
$ cd jb/project/docker
|
||||
$ docker build .
|
||||
...
|
||||
Successfully built 942ea9900054
|
||||
```
|
||||
Run these commands in the new container:
|
||||
```
|
||||
$ docker run -v `pwd`../../../../:/JetBrainsRuntime -it 942ea9900054
|
||||
# cd /JetBrainsRuntime
|
||||
# sh ./configure
|
||||
# make images CONF=linux-x86_64-normal-server-release
|
||||
```
|
||||
|
||||
### Ubuntu Linux
|
||||
Install the necessary tools, libraries, and headers with:
|
||||
```
|
||||
$ sudo apt-get install autoconf make build-essential libx11-dev libxext-dev libxrender-dev libxtst-dev \
|
||||
libxt-dev libxrandr-dev libcups2-dev libfontconfig1-dev libasound2-dev \
|
||||
openjdk-11-jdk
|
||||
```
|
||||
Then run the following:
|
||||
```
|
||||
$ cd JetBrainsRuntime
|
||||
$ sh ./configure --disable-warnings-as-errors
|
||||
$ make images
|
||||
```
|
||||
|
||||
### Windows
|
||||
<a name="build-windows"></a>
|
||||
Install the following:
|
||||
* [Cygwin x64](http://www.cygwin.com/).
|
||||
Required packages: `autoconf`, `binutils`, `cpio`, `diffutils`, `file`, `gawk`, `gcc-core`, `make`, `m4`, `unzip`, `zip`.
|
||||
Install those together with Cygwin.
|
||||
* [Visual Studio compiler toolset](https://visualstudio.microsoft.com/downloads/).
|
||||
Install with the desktop development kit, which includes Windows SDK and compilers.
|
||||
Visual Studio 2015 is supported by default.
|
||||
* [Java 11](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html).
|
||||
If you have problems while configuring, read [Java tips on Cygwin](http://horstmann.com/articles/cygwin-tips.html).
|
||||
|
||||
From the command line:
|
||||
```
|
||||
"c:\Program Files (x86)\Microsoft Visual Studio 15.0\VC\vcvarsall.bat" amd64
|
||||
c:\cygwin64\bin\mintty.exe /usr/bin/bash -l
|
||||
```
|
||||
The first command sets up environment variables, the second starts a Cygwin shell with the proper environment.
|
||||
|
||||
In the Cygwin shell:
|
||||
```
|
||||
$ cd JetBrainsRuntime
|
||||
$ bash configure --enable-option-checking=fatal --with-toolchain-version=2015 \
|
||||
--with-boot-jdk="/cygdrive/c/Program Files/Java/jdk-11.0.5" --disable-warnings-as-errors
|
||||
$ make images
|
||||
```
|
||||
|
||||
### macOS
|
||||
Install the following:
|
||||
* Xcode command line developer tools and `autoconf` via [Homebrew](getDpiInfo).
|
||||
* [Java 11](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html).
|
||||
|
||||
From the command line:
|
||||
```
|
||||
$ cd JetBrainsRuntime
|
||||
$ sh ./configure --prefix=$(pwd)/build --disable-warnings-as-errors
|
||||
$ make images
|
||||
```
|
||||
|
||||
## Developing
|
||||
You can use [CLion](https://www.jetbrains.com/clion/) to develop native parts of the JetBrains Runtime and
|
||||
[IntelliJ IDEA](https://www.jetbrains.com/idea/) for the parts written in Java.
|
||||
Both require projects to be created.
|
||||
|
||||
### CLion
|
||||
Run
|
||||
```
|
||||
$ make compile-commands
|
||||
```
|
||||
in the git root and open the resulting `build/.../compile_commands.json` file as a project.
|
||||
Then use `Tools | Compilation Database | Change Project Root` to point to git root of this repository.
|
||||
|
||||
See also this detailed step-by-step tutorial for all platforms:
|
||||
[How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
|
||||
|
||||
### IDEA
|
||||
Run
|
||||
```
|
||||
$ sh ./bin/idea.sh
|
||||
```
|
||||
in the git root to generate project files (add `--help` for options). Then open the git root directory
|
||||
as a project in IDEA.
|
||||
|
||||
## Contributing
|
||||
We are happy to receive your pull requests!
|
||||
Before you submit one, please sign our [Contributor License Agreement (CLA)](https://www.jetbrains.com/agreements/cla/).
|
||||
|
||||
## Resources
|
||||
* [JetBrains Runtime on github](https://github.com/JetBrains/JetBrainsRuntime).
|
||||
* [OpenJDK build instructions](http://hg.openjdk.java.net/jdk/jdk11/raw-file/tip/doc/building.html).
|
||||
* [OpenJDK test instructions](http://hg.openjdk.java.net/jdk/jdk11/raw-file/tip/doc/building.html#running-tests).
|
||||
* [How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
|
||||
|
||||
@@ -7,6 +7,12 @@ do_reset_dcevm=0
|
||||
HEAD_REVISION=0
|
||||
WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS="--with-native-debug-symbols=zipped"
|
||||
|
||||
STATIC_CONF_ARGS=""
|
||||
common_conf_props_file="jb/project/tools/common/static_conf_args.txt"
|
||||
if [[ -f "$common_conf_props_file" ]]; then
|
||||
STATIC_CONF_ARGS=$(<$common_conf_props_file)
|
||||
fi
|
||||
|
||||
function zip_native_debug_symbols() {
|
||||
image_bundle_path=$(echo $1 | cut -d"/" -f-4)
|
||||
jbr_diz_name=$2
|
||||
|
||||
1
jb/project/tools/common/static_conf_args.txt
Normal file
1
jb/project/tools/common/static_conf_args.txt
Normal file
@@ -0,0 +1 @@
|
||||
--with-vendor-vm-bug-url=https://youtrack.jetbrains.com/issues/JBR
|
||||
@@ -57,6 +57,7 @@ function do_configure {
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
$WITH_IMPORT_MODULES \
|
||||
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || do_exit $?
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ function do_configure {
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
$WITH_IMPORT_MODULES \
|
||||
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || do_exit $?
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ linux32 bash configure \
|
||||
--with-version-opt=b${build_number} \
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || exit $?
|
||||
make clean CONF=linux-x86-normal-server-release || exit $?
|
||||
make images CONF=linux-x86-normal-server-release test-image || exit $?
|
||||
|
||||
@@ -70,6 +70,7 @@ function do_configure {
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
--disable-hotspot-gtest --disable-javac-server --disable-full-docs --disable-manpages \
|
||||
--enable-cds=no \
|
||||
$STATIC_CONF_ARGS \
|
||||
$WITH_JVM_FEATURES \
|
||||
--with-extra-cflags="-F$(pwd)/Frameworks" \
|
||||
--with-extra-cxxflags="-F$(pwd)/Frameworks" \
|
||||
@@ -87,6 +88,7 @@ function do_configure {
|
||||
$WITH_IMPORT_MODULES \
|
||||
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || do_exit $?
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ function do_configure {
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
--with-build-jdk=${BOOT_JDK} \
|
||||
--disable-ccache \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || do_exit $?
|
||||
}
|
||||
|
||||
@@ -100,32 +101,32 @@ function create_jbr {
|
||||
cat ${JSDK}/release | tr -d '\r' | grep -v 'JAVA_VERSION' | grep -v 'MODULES' >> ${JBR_BUNDLE}/release
|
||||
}
|
||||
|
||||
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
|
||||
JBRSDK_BASE_NAME=jbrsdk_${bundle_type}-${JBSDK_VERSION}
|
||||
WITH_DEBUG_LEVEL="--with-debug-level=release"
|
||||
RELEASE_NAME=windows-aarch64-normal-server-release
|
||||
JBSDK=${JBRSDK_BASE_NAME}-windows-aarch64-b${build_number}
|
||||
case "$bundle_type" in
|
||||
"jcef")
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_arch64.patch || do_exit $?
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
do_reset_changes=1
|
||||
;;
|
||||
"dcevm")
|
||||
HEAD_REVISION=$(git rev-parse HEAD)
|
||||
git am jb/project/tools/patches/dcevm/*.patch || do_exit $?
|
||||
do_reset_dcevm=1
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_arch64.patch || do_exit $?
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
do_reset_changes=1
|
||||
;;
|
||||
"nomod")
|
||||
WITH_IMPORT_MODULES=""
|
||||
;;
|
||||
"fd")
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_arch64.patch || do_exit $?
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
do_reset_changes=1
|
||||
WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
|
||||
RELEASE_NAME=windows-aarch64-normal-server-fastdebug
|
||||
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}-fastdebug
|
||||
JBSDK=${JBRSDK_BASE_NAME}-windows-aarch64-fastdebug-b${build_number}
|
||||
JBSDK=jbrsdk-${JBSDK_VERSION}-windows-aarch64-fastdebug-b${build_number}
|
||||
;;
|
||||
*)
|
||||
echo "***ERR*** bundle was not specified" && do_exit 1
|
||||
@@ -134,22 +135,27 @@ esac
|
||||
|
||||
if [ -z "$INC_BUILD" ]; then
|
||||
do_configure || do_exit $?
|
||||
if [ "${bundle_type}" == "jcef" ]; then
|
||||
make LOG=info clean images test-image CONF=$RELEASE_NAME || do_exit $?
|
||||
if [ "${bundle_type}" == "dcevm" ]; then
|
||||
make LOG=info CONF=$RELEASE_NAME clean images test-image || do_exit $?
|
||||
else
|
||||
make LOG=info clean images CONF=$RELEASE_NAME || do_exit $?
|
||||
make LOG=info CONF=$RELEASE_NAME clean images || do_exit $?
|
||||
fi
|
||||
else
|
||||
if [ "${bundle_type}" == "jcef" ]; then
|
||||
make LOG=info images test-image CONF=$RELEASE_NAME || do_exit $?
|
||||
if [ "${bundle_type}" == "dcevm" ]; then
|
||||
make LOG=info CONF=$RELEASE_NAME images test-image || do_exit $?
|
||||
else
|
||||
make LOG=info images CONF=$RELEASE_NAME || do_exit $?
|
||||
make LOG=info CONF=$RELEASE_NAME images || do_exit $?
|
||||
fi
|
||||
fi
|
||||
|
||||
JSDK=build/$RELEASE_NAME/images/jdk
|
||||
BASE_DIR=build/$RELEASE_NAME/images
|
||||
JBRSDK_BUNDLE=jbrsdk
|
||||
BASE_DIR=jre
|
||||
if [ "${bundle_type}" == "fd" ]; then
|
||||
JBRSDK_BUNDLE=jbrsdk
|
||||
else
|
||||
JBRSDK_BUNDLE=jbrsdk_${bundle_type}
|
||||
fi
|
||||
|
||||
rm -rf ${BASE_DIR}/${JBRSDK_BUNDLE} && rsync -a --exclude demo --exclude sample ${JSDK}/ ${JBRSDK_BUNDLE} || do_exit $?
|
||||
if [[ "${bundle_type}" == *jcef* ]] || [[ "${bundle_type}" == *dcevm* ]] || [[ "${bundle_type}" == fd ]]
|
||||
|
||||
@@ -60,6 +60,7 @@ function do_configure {
|
||||
--with-toolchain-version=${TOOLCHAIN_VERSION} \
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
--disable-ccache \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || do_exit $?
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ PATH="/usr/local/bin:/usr/bin:${PATH}"
|
||||
--with-toolchain-version=${TOOLCHAIN_VERSION} \
|
||||
--with-boot-jdk=${BOOT_JDK} \
|
||||
--disable-ccache \
|
||||
$STATIC_CONF_ARGS \
|
||||
--enable-cds=yes || exit 1
|
||||
make clean CONF=windows-x86-normal-server-release || exit 1
|
||||
make LOG=info images CONF=windows-x86-normal-server-release test-image || exit 1
|
||||
|
||||
@@ -51,7 +51,7 @@ function pack_jbr {
|
||||
/usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR jbr || do_exit $?
|
||||
}
|
||||
|
||||
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
|
||||
JBRSDK_BASE_NAME=jbrsdk_${bundle_type}-${JBSDK_VERSION}
|
||||
WITH_DEBUG_LEVEL="--with-debug-level=release"
|
||||
RELEASE_NAME=windows-aarch64-normal-server-release
|
||||
JBSDK=${JBRSDK_BASE_NAME}-windows-aarch64-b${build_number}
|
||||
@@ -59,25 +59,27 @@ case "$bundle_type" in
|
||||
"fd")
|
||||
WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
|
||||
RELEASE_NAME=windows-aarch64-normal-server-fastdebug
|
||||
JBSDK=${JBRSDK_BASE_NAME}-windows-aarch64-fastdebug-b${build_number}
|
||||
JBSDK=jbrsdk-${JBSDK_VERSION}-windows-aarch64-fastdebug-b${build_number}
|
||||
;;
|
||||
esac
|
||||
|
||||
IMAGES_DIR=build/$RELEASE_NAME/images
|
||||
JSDK=$IMAGES_DIR/jdk
|
||||
BASE_DIR=.
|
||||
|
||||
if [ "${bundle_type}" == "dcevm" ] || [ "${bundle_type}" == "fd" ]; then
|
||||
if [ "${bundle_type}" == "fd" ]; then
|
||||
JBRSDK_BUNDLE=jbrsdk
|
||||
echo Creating $JBSDK.tar.gz ...
|
||||
[ -f "$JBSDK.tar.gz" ] && rm "$JBSDK.tar.gz"
|
||||
/usr/bin/tar -czf $JBSDK.tar.gz $JBRSDK_BUNDLE || do_exit $?
|
||||
else
|
||||
JBRSDK_BUNDLE=jbrsdk_${bundle_type}
|
||||
fi
|
||||
|
||||
echo Creating $JBSDK.tar.gz ...
|
||||
[ -f "$JBSDK.tar.gz" ] && rm "$JBSDK.tar.gz"
|
||||
/usr/bin/tar -czf $JBSDK.tar.gz $JBRSDK_BUNDLE || do_exit $?
|
||||
|
||||
pack_jbr $bundle_type
|
||||
|
||||
if [ "$bundle_type" == "dcevm" ]; then
|
||||
JBRSDK_TEST=$JBRSDK_BASE_NAME-windows-test-aarch64-b$build_number
|
||||
JBRSDK_TEST=jbrsdk-${JBSDK_VERSION}-windows-test-aarch64-b$build_number
|
||||
echo Creating $JBRSDK_TEST.tar.gz ...
|
||||
/usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
|
||||
fi
|
||||
@@ -28,12 +28,12 @@
|
||||
|
||||
DEFAULT_VERSION_FEATURE=11
|
||||
DEFAULT_VERSION_INTERIM=0
|
||||
DEFAULT_VERSION_UPDATE=15
|
||||
DEFAULT_VERSION_UPDATE=16
|
||||
DEFAULT_VERSION_PATCH=0
|
||||
DEFAULT_VERSION_EXTRA1=0
|
||||
DEFAULT_VERSION_EXTRA2=0
|
||||
DEFAULT_VERSION_EXTRA3=0
|
||||
DEFAULT_VERSION_DATE=2022-04-19
|
||||
DEFAULT_VERSION_DATE=2022-07-19
|
||||
DEFAULT_VERSION_CLASSFILE_MAJOR=55 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="10 11"
|
||||
|
||||
@@ -52,6 +52,7 @@ allfonts.myanmar=Myanmar Text
|
||||
allfonts.dingbats=Wingdings
|
||||
allfonts.symbol=Symbol
|
||||
allfonts.symbols=Segoe UI Symbol
|
||||
allfonts.emoji=Segoe UI Emoji
|
||||
allfonts.thai=Tahoma
|
||||
allfonts.georgian=Sylfaen
|
||||
|
||||
@@ -197,58 +198,58 @@ dialoginput.bolditalic.korean=Malgun Gothic
|
||||
|
||||
# Search Sequences
|
||||
|
||||
sequence.allfonts=alphabetic/default,dingbats,symbol
|
||||
sequence.allfonts=alphabetic/default,dingbats,symbol,symbols
|
||||
|
||||
sequence.serif.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb
|
||||
sequence.sansserif.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb
|
||||
sequence.monospaced.GBK=chinese-ms936,alphabetic,dingbats,symbol,chinese-ms936-extb
|
||||
sequence.dialog.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb
|
||||
sequence.dialoginput.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb
|
||||
sequence.serif.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb,symbols
|
||||
sequence.sansserif.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb,symbols
|
||||
sequence.monospaced.GBK=chinese-ms936,alphabetic,dingbats,symbol,chinese-ms936-extb,symbols
|
||||
sequence.dialog.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb,symbols
|
||||
sequence.dialoginput.GBK=alphabetic,chinese-ms936,dingbats,symbol,chinese-ms936-extb,symbols
|
||||
|
||||
sequence.serif.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb
|
||||
sequence.sansserif.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb
|
||||
sequence.monospaced.GB18030=chinese-gb18030,alphabetic,dingbats,symbol,chinese-gb18030-extb
|
||||
sequence.dialog.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb
|
||||
sequence.dialoginput.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb
|
||||
sequence.serif.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb,symbols
|
||||
sequence.sansserif.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb,symbols
|
||||
sequence.monospaced.GB18030=chinese-gb18030,alphabetic,dingbats,symbol,chinese-gb18030-extb,symbols
|
||||
sequence.dialog.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb,symbols
|
||||
sequence.dialoginput.GB18030=alphabetic,chinese-gb18030,dingbats,symbol,chinese-gb18030-extb,symbols
|
||||
|
||||
sequence.serif.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.sansserif.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.monospaced.x-windows-950=chinese-ms950,alphabetic,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialog.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialoginput.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.serif.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.sansserif.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.monospaced.x-windows-950=chinese-ms950,alphabetic,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialog.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialoginput.x-windows-950=alphabetic,chinese-ms950,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
|
||||
sequence.serif.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.sansserif.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.monospaced.x-MS950-HKSCS=chinese-ms950,alphabetic,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialog.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialoginput.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.serif.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.sansserif.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.monospaced.x-MS950-HKSCS=chinese-ms950,alphabetic,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialog.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialoginput.x-MS950-HKSCS=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
|
||||
sequence.serif.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.sansserif.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.monospaced.x-MS950-HKSCS-XP=chinese-ms950,alphabetic,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialog.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.dialoginput.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb
|
||||
sequence.serif.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.sansserif.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.monospaced.x-MS950-HKSCS-XP=chinese-ms950,alphabetic,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialog.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
sequence.dialoginput.x-MS950-HKSCS-XP=alphabetic,chinese-ms950,chinese-hkscs,dingbats,symbol,chinese-ms950-extb,symbols
|
||||
|
||||
sequence.allfonts.UTF-8.hi=alphabetic/1252,devanagari,dingbats,symbol
|
||||
sequence.allfonts.UTF-8.ja=alphabetic,japanese,devanagari,dingbats,symbol
|
||||
sequence.allfonts.UTF-8.hi=alphabetic/1252,devanagari,dingbats,symbol,symbols
|
||||
sequence.allfonts.UTF-8.ja=alphabetic,japanese,devanagari,dingbats,symbol,symbols
|
||||
|
||||
sequence.allfonts.windows-1255=hebrew,alphabetic/1252,dingbats,symbol
|
||||
sequence.allfonts.windows-1255=hebrew,alphabetic/1252,dingbats,symbol,symbols
|
||||
|
||||
sequence.serif.windows-31j=alphabetic,japanese,dingbats,symbol
|
||||
sequence.sansserif.windows-31j=alphabetic,japanese,dingbats,symbol
|
||||
sequence.monospaced.windows-31j=japanese,alphabetic,dingbats,symbol
|
||||
sequence.dialog.windows-31j=alphabetic,japanese,dingbats,symbol
|
||||
sequence.dialoginput.windows-31j=alphabetic,japanese,dingbats,symbol
|
||||
sequence.serif.windows-31j=alphabetic,japanese,dingbats,symbol,symbols
|
||||
sequence.sansserif.windows-31j=alphabetic,japanese,dingbats,symbol,symbols
|
||||
sequence.monospaced.windows-31j=japanese,alphabetic,dingbats,symbol,symbols
|
||||
sequence.dialog.windows-31j=alphabetic,japanese,dingbats,symbol,symbols
|
||||
sequence.dialoginput.windows-31j=alphabetic,japanese,dingbats,symbol,symbols
|
||||
|
||||
sequence.serif.x-windows-949=alphabetic,korean,dingbats,symbol
|
||||
sequence.sansserif.x-windows-949=alphabetic,korean,dingbats,symbol
|
||||
sequence.monospaced.x-windows-949=korean,alphabetic,dingbats,symbol
|
||||
sequence.dialog.x-windows-949=alphabetic,korean,dingbats,symbol
|
||||
sequence.dialoginput.x-windows-949=alphabetic,korean,dingbats,symbol
|
||||
sequence.serif.x-windows-949=alphabetic,korean,dingbats,symbol,symbols
|
||||
sequence.sansserif.x-windows-949=alphabetic,korean,dingbats,symbol,symbols
|
||||
sequence.monospaced.x-windows-949=korean,alphabetic,dingbats,symbol,symbols
|
||||
sequence.dialog.x-windows-949=alphabetic,korean,dingbats,symbol,symbols
|
||||
sequence.dialoginput.x-windows-949=alphabetic,korean,dingbats,symbol,symbols
|
||||
|
||||
sequence.allfonts.x-windows-874=alphabetic,thai,dingbats,symbol
|
||||
sequence.allfonts.x-windows-874=alphabetic,thai,dingbats,symbol,symbols
|
||||
|
||||
sequence.fallback=symbols,\
|
||||
sequence.fallback=emoji,\
|
||||
chinese-ms950,chinese-hkscs,chinese-ms936,chinese-gb18030,\
|
||||
japanese,korean,chinese-ms950-extb,chinese-ms936-extb,\
|
||||
georgian,devanagari,bengali,gujarati,gurmukhi,kannada,\
|
||||
@@ -260,6 +261,25 @@ sequence.fallback=symbols,\
|
||||
exclusion.alphabetic=0700-1cff,1d80-1e9f,1f00-2017,2020-20ab,20ad-20b8,20bb-20bc,20be-24ff,2501-2501,2503-250b,250d-250f,2511-2513,2515-2517,2519-251b,251d-2523,2525-252b,252d-2533,2535-253b,253d-254f,256d-f8ff
|
||||
exclusion.chinese-gb18030=0390-03d6,2200-22ef,2701-27be
|
||||
exclusion.hebrew=0041-005a,0060-007a,007f-00ff,20ac-20ac
|
||||
exclusion.symbols=000d-0022,0024-0029,002b-002f,003a-00a8,00aa-00ad,00af-02dc,2002-2003,\
|
||||
2005-2005,200d-200d,2013-2014,2018-201a,201c-201e,2020-2022,2026-2026,\
|
||||
2030-2030,2039-203a,2044-2044,20ac-20ac,20e3-20e3,2126-2126,2190-2193,\
|
||||
219a-21a8,21ab-21ff,2206-2206,220f-220f,2211-2212,2219-221a,221e-221e,\
|
||||
222b-222b,2248-2248,2260-2260,231a-231b,23e9-23ec,23f0-23f0,23f3-23f3,\
|
||||
24b6-24c1,24c3-24cf,25a0-25a9,25ac-25b5,25b7-25bf,25c1-25fa,25fd-25ff,\
|
||||
260f-2610,2612-2612,2614-2615,263b-263b,263f-263f,2641-2641,2643-2653,\
|
||||
2672-267a,267c-267d,267f-267f,2693-2693,26a1-26a6,26a8-26ab,26bd-26be,\
|
||||
26c4-26c5,26c7-26c7,26ce-26ce,26d4-26d4,26dd-26dd,26e3-26e3,26ea-26ea,\
|
||||
26f2-26f3,26f5-26f5,26fa-26fa,26fd-26fd,2701-2701,2703-2705,270a-270b,\
|
||||
270e-270e,2710-2710,2713-2713,2715-2715,2717-2718,2728-2728,2731-2732,\
|
||||
2735-2743,2745-2746,2748-274c,274e-274e,2753-2755,2757-2757,2795-2797,\
|
||||
27b0-27b0,27bf-27bf,2936-2937,2b12-2b1c,2b50-2b52,2b55-2b55,3244-3247,\
|
||||
01f000-01f02b,01f0cf-01f0cf,01f172-01f17d,01f180-01f1ff,01f201-01f201,\
|
||||
01f210-01f236,01f238-01f23a,01f250-01f320,01f32d-01f335,01f337-01f37c,\
|
||||
01f37e-01f393,01f3a0-01f3ca,01f3cf-01f3d3,01f3e0-01f3f0,01f3f4-01f3f4,\
|
||||
01f3f8-01f43e,01f440-01f440,01f442-01f4fc,01f4ff-01f53d,01f54b-01f567,\
|
||||
01f57a-01f57a,01f595-01f596,01f5a4-01f5a4,01f5fb-01f64f,01f680-01f6c5,\
|
||||
01f6cc-01f6cc,01f6d0-01f6d2,01f6eb-01f6ec,01f6f4-01f6f6,01f910-01f93a,01f93c-01f9c0
|
||||
|
||||
# Monospaced to Proportional width variant mapping
|
||||
# (Experimental private syntax)
|
||||
@@ -323,3 +343,4 @@ filename.Wingdings=WINGDING.TTF
|
||||
|
||||
filename.Sylfaen=sylfaen.ttf
|
||||
filename.Segoe_UI_Symbol=SEGUISYM.TTF
|
||||
filename.Segoe_UI_Emoji=seguiemj.ttf
|
||||
|
||||
@@ -586,7 +586,6 @@ ciKlass* ciEnv::get_klass_by_index(const constantPoolHandle& cpool,
|
||||
ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
int pool_index, int cache_index,
|
||||
ciInstanceKlass* accessor) {
|
||||
bool ignore_will_link;
|
||||
EXCEPTION_CONTEXT;
|
||||
int index = pool_index;
|
||||
if (cache_index >= 0) {
|
||||
@@ -658,8 +657,8 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
return ciConstant(T_OBJECT, constant);
|
||||
}
|
||||
} else if (tag.is_klass() || tag.is_unresolved_klass()) {
|
||||
// 4881222: allow ldc to take a class type
|
||||
ciKlass* klass = get_klass_by_index_impl(cpool, index, ignore_will_link, accessor);
|
||||
bool will_link;
|
||||
ciKlass* klass = get_klass_by_index_impl(cpool, index, will_link, accessor);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
record_out_of_memory_failure();
|
||||
@@ -667,7 +666,8 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
}
|
||||
assert (klass->is_instance_klass() || klass->is_array_klass(),
|
||||
"must be an instance or array klass ");
|
||||
return ciConstant(T_OBJECT, klass->java_mirror());
|
||||
ciInstance* mirror = (will_link ? klass->java_mirror() : get_unloaded_klass_mirror(klass));
|
||||
return ciConstant(T_OBJECT, mirror);
|
||||
} else if (tag.is_method_type()) {
|
||||
// must execute Java code to link this CP entry into cache[i].f1
|
||||
ciSymbol* signature = get_symbol(cpool->method_type_signature_at(index));
|
||||
@@ -675,6 +675,7 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
return ciConstant(T_OBJECT, ciobj);
|
||||
} else if (tag.is_method_handle()) {
|
||||
// must execute Java code to link this CP entry into cache[i].f1
|
||||
bool ignore_will_link;
|
||||
int ref_kind = cpool->method_handle_ref_kind_at(index);
|
||||
int callee_index = cpool->method_handle_klass_index_at(index);
|
||||
ciKlass* callee = get_klass_by_index_impl(cpool, callee_index, ignore_will_link, accessor);
|
||||
|
||||
@@ -1698,10 +1698,32 @@ void LinkResolver::resolve_handle_call(CallInfo& result,
|
||||
assert(resolved_klass == SystemDictionary::MethodHandle_klass() ||
|
||||
resolved_klass == SystemDictionary::VarHandle_klass(), "");
|
||||
assert(MethodHandles::is_signature_polymorphic_name(link_info.name()), "");
|
||||
Handle resolved_appendix;
|
||||
Handle resolved_method_type;
|
||||
Handle resolved_appendix;
|
||||
Handle resolved_method_type;
|
||||
methodHandle resolved_method = lookup_polymorphic_method(link_info,
|
||||
&resolved_appendix, &resolved_method_type, CHECK);
|
||||
&resolved_appendix, &resolved_method_type, CHECK);
|
||||
|
||||
if (link_info.check_access()) {
|
||||
Symbol* name = link_info.name();
|
||||
vmIntrinsics::ID iid = MethodHandles::signature_polymorphic_name_id(name);
|
||||
if (MethodHandles::is_signature_polymorphic_intrinsic(iid)) {
|
||||
// Check if method can be accessed by the referring class.
|
||||
// MH.linkTo* invocations are not rewritten to invokehandle.
|
||||
assert(iid == vmIntrinsics::_invokeBasic, "%s", vmIntrinsics::name_at(iid));
|
||||
|
||||
Klass* current_klass = link_info.current_klass();
|
||||
assert(current_klass != NULL , "current_klass should not be null");
|
||||
check_method_accessability(current_klass,
|
||||
resolved_klass,
|
||||
resolved_method->method_holder(),
|
||||
resolved_method,
|
||||
CHECK);
|
||||
} else {
|
||||
// Java code is free to arbitrarily link signature-polymorphic invokers.
|
||||
assert(iid == vmIntrinsics::_invokeGeneric, "not an invoker: %s", vmIntrinsics::name_at(iid));
|
||||
assert(MethodHandles::is_signature_polymorphic_public_name(resolved_klass, name), "not public");
|
||||
}
|
||||
}
|
||||
result.set_handle(resolved_klass, resolved_method, resolved_appendix, resolved_method_type, CHECK);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -137,7 +137,7 @@ class HostPortrange {
|
||||
}
|
||||
this.ipv4 = this.literal = ipv4;
|
||||
if (ipv4) {
|
||||
byte[] ip = IPAddressUtil.textToNumericFormatV4(hoststr);
|
||||
byte[] ip = IPAddressUtil.validateNumericFormatV4(hoststr);
|
||||
if (ip == null) {
|
||||
throw new IllegalArgumentException("illegal IPv4 address");
|
||||
}
|
||||
|
||||
@@ -1089,7 +1089,11 @@ class InetAddress implements java.io.Serializable {
|
||||
private byte [] createAddressByteArray(String addrStr) {
|
||||
byte[] addrArray;
|
||||
// check if IPV4 address - most likely
|
||||
addrArray = IPAddressUtil.textToNumericFormatV4(addrStr);
|
||||
try {
|
||||
addrArray = IPAddressUtil.validateNumericFormatV4(addrStr);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return null;
|
||||
}
|
||||
if (addrArray == null) {
|
||||
addrArray = IPAddressUtil.textToNumericFormatV6(addrStr);
|
||||
}
|
||||
@@ -1324,13 +1328,19 @@ class InetAddress implements java.io.Serializable {
|
||||
}
|
||||
|
||||
// if host is an IP address, we won't do further lookup
|
||||
if (Character.digit(host.charAt(0), 16) != -1
|
||||
if (IPAddressUtil.digit(host.charAt(0), 16) != -1
|
||||
|| (host.charAt(0) == ':')) {
|
||||
byte[] addr = null;
|
||||
byte[] addr;
|
||||
int numericZone = -1;
|
||||
String ifname = null;
|
||||
// see if it is IPv4 address
|
||||
addr = IPAddressUtil.textToNumericFormatV4(host);
|
||||
try {
|
||||
addr = IPAddressUtil.validateNumericFormatV4(host);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
var uhe = new UnknownHostException(host);
|
||||
uhe.initCause(iae);
|
||||
throw uhe;
|
||||
}
|
||||
if (addr == null) {
|
||||
// This is supposed to be an IPv6 literal
|
||||
// Check if a numeric or string zone id is present
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -463,7 +463,7 @@ public final class SocketPermission extends Permission
|
||||
if (!host.isEmpty()) {
|
||||
// see if we are being initialized with an IP address.
|
||||
char ch = host.charAt(0);
|
||||
if (ch == ':' || Character.digit(ch, 16) != -1) {
|
||||
if (ch == ':' || IPAddressUtil.digit(ch, 16) != -1) {
|
||||
byte ip[] = IPAddressUtil.textToNumericFormatV4(host);
|
||||
if (ip == null) {
|
||||
ip = IPAddressUtil.textToNumericFormatV6(host);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -30,6 +30,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import jdk.internal.org.xml.sax.InputSource;
|
||||
@@ -42,7 +43,11 @@ public abstract class Parser {
|
||||
|
||||
public static final String FAULT = "";
|
||||
protected static final int BUFFSIZE_READER = 512;
|
||||
// Initial buffer (mBuff) size
|
||||
protected static final int BUFFSIZE_PARSER = 128;
|
||||
// Max buffer size
|
||||
private static final int MAX_ARRAY_SIZE = 1024 << 16;
|
||||
|
||||
/**
|
||||
* The end of stream character.
|
||||
*/
|
||||
@@ -525,6 +530,10 @@ public abstract class Parser {
|
||||
mPh = PH_DTD; // DTD
|
||||
for (short st = 0; st >= 0;) {
|
||||
ch = getch();
|
||||
// report error if EOS is reached while parsing the DTD
|
||||
if (ch == EOS) {
|
||||
panic(FAULT);
|
||||
}
|
||||
switch (st) {
|
||||
case 0: // read the document type name
|
||||
if (chtyp(ch) != ' ') {
|
||||
@@ -1664,6 +1673,10 @@ public abstract class Parser {
|
||||
mBuffIdx = -1;
|
||||
for (short st = 0; st >= 0;) {
|
||||
ch = getch();
|
||||
// report error if EOS is reached while parsing the DTD
|
||||
if (ch == EOS) {
|
||||
panic(FAULT);
|
||||
}
|
||||
switch (st) {
|
||||
case 0: // the first '[' of the CDATA open
|
||||
if (ch == '[') {
|
||||
@@ -1871,7 +1884,7 @@ public abstract class Parser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resoves an entity.
|
||||
* Resolves an entity.
|
||||
*
|
||||
* This method resolves built-in and character entity references. It is also
|
||||
* reports external entities to the application.
|
||||
@@ -2529,7 +2542,7 @@ public abstract class Parser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single or double quotted string in to the buffer.
|
||||
* Reads a single or double quoted string into the buffer.
|
||||
*
|
||||
* This method resolves entities inside a string unless the parser parses
|
||||
* DTD.
|
||||
@@ -2664,7 +2677,7 @@ public abstract class Parser {
|
||||
* @param ch The character to append to the buffer.
|
||||
* @param mode The normalization mode.
|
||||
*/
|
||||
private void bappend(char ch, char mode) {
|
||||
private void bappend(char ch, char mode) throws Exception {
|
||||
// This implements attribute value normalization as
|
||||
// described in the XML specification [#3.3.3].
|
||||
switch (mode) {
|
||||
@@ -2714,16 +2727,9 @@ public abstract class Parser {
|
||||
*
|
||||
* @param ch The character to append to the buffer.
|
||||
*/
|
||||
private void bappend(char ch) {
|
||||
try {
|
||||
mBuff[++mBuffIdx] = ch;
|
||||
} catch (Exception exp) {
|
||||
// Double the buffer size
|
||||
char buff[] = new char[mBuff.length << 1];
|
||||
System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
|
||||
mBuff = buff;
|
||||
mBuff[mBuffIdx] = ch;
|
||||
}
|
||||
private void bappend(char ch) throws Exception {
|
||||
ensureCapacity(++mBuffIdx);
|
||||
mBuff[mBuffIdx] = ch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2733,14 +2739,9 @@ public abstract class Parser {
|
||||
* @param cidx The character buffer (mChars) start index.
|
||||
* @param bidx The parser buffer (mBuff) start index.
|
||||
*/
|
||||
private void bcopy(int cidx, int bidx) {
|
||||
private void bcopy(int cidx, int bidx) throws Exception {
|
||||
int length = mChIdx - cidx;
|
||||
if ((bidx + length + 1) >= mBuff.length) {
|
||||
// Expand the buffer
|
||||
char buff[] = new char[mBuff.length + length];
|
||||
System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
|
||||
mBuff = buff;
|
||||
}
|
||||
ensureCapacity(bidx + length + 1);
|
||||
System.arraycopy(mChars, cidx, mBuff, bidx, length);
|
||||
mBuffIdx += length;
|
||||
}
|
||||
@@ -3327,7 +3328,7 @@ public abstract class Parser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrives the next character in the document.
|
||||
* Retrieves the next character in the document.
|
||||
*
|
||||
* @return The next character in the document.
|
||||
*/
|
||||
@@ -3433,4 +3434,23 @@ public abstract class Parser {
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int minCapacity) throws Exception {
|
||||
if (mBuff == null) {
|
||||
int newCapacity = minCapacity > BUFFSIZE_PARSER ?
|
||||
minCapacity + BUFFSIZE_PARSER : BUFFSIZE_PARSER;
|
||||
mBuff = new char[newCapacity];
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBuff.length <= minCapacity) {
|
||||
int size = mBuff.length << 1;
|
||||
int newCapacity = size > minCapacity ? size : minCapacity + BUFFSIZE_PARSER;
|
||||
if (newCapacity < 0 || newCapacity > MAX_ARRAY_SIZE) {
|
||||
panic(FAULT);
|
||||
}
|
||||
|
||||
mBuff = Arrays.copyOf(mBuff, newCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -28,13 +28,15 @@ package sun.net.util;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
|
||||
import java.io.IOException;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.nio.CharBuffer;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
@@ -102,7 +104,7 @@ public class IPAddressUtil {
|
||||
tmpValue = 0;
|
||||
newOctet = true;
|
||||
} else {
|
||||
int digit = Character.digit(c, 10);
|
||||
int digit = digit(c, 10);
|
||||
if (digit < 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -127,6 +129,29 @@ public class IPAddressUtil {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if input string is a valid IPv4 address literal.
|
||||
* If the "jdk.net.allowAmbiguousIPAddressLiterals" system property is set
|
||||
* to {@code false}, or is not set then validation of the address string is performed as follows:
|
||||
* If string can't be parsed by following IETF IPv4 address string literals
|
||||
* formatting style rules (default one), but can be parsed by following BSD formatting
|
||||
* style rules, the IPv4 address string content is treated as ambiguous and
|
||||
* {@code IllegalArgumentException} is thrown.
|
||||
*
|
||||
* @param src input string
|
||||
* @return bytes array if string is a valid IPv4 address string
|
||||
* @throws IllegalArgumentException if "jdk.net.allowAmbiguousIPAddressLiterals" SP is set to
|
||||
* "false" and IPv4 address string {@code "src"} is ambiguous
|
||||
*/
|
||||
public static byte[] validateNumericFormatV4(String src) {
|
||||
byte[] parsedBytes = textToNumericFormatV4(src);
|
||||
if (!ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE
|
||||
&& parsedBytes == null && isBsdParsableV4(src)) {
|
||||
throw new IllegalArgumentException("Invalid IP address literal: " + src);
|
||||
}
|
||||
return parsedBytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert IPv6 presentation level address to network order binary form.
|
||||
* credit:
|
||||
@@ -172,7 +197,7 @@ public class IPAddressUtil {
|
||||
val = 0;
|
||||
while (i < srcb_length) {
|
||||
ch = srcb[i++];
|
||||
int chval = Character.digit(ch, 16);
|
||||
int chval = digit(ch, 16);
|
||||
if (chval != -1) {
|
||||
val <<= 4;
|
||||
val |= chval;
|
||||
@@ -553,4 +578,249 @@ public class IPAddressUtil {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric value of the character {@code ch} in the
|
||||
* specified radix.
|
||||
*
|
||||
* @param ch the character to be converted.
|
||||
* @param radix the radix.
|
||||
* @return the numeric value represented by the character in the
|
||||
* specified radix.
|
||||
*/
|
||||
public static int digit(char ch, int radix) {
|
||||
if (ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE) {
|
||||
return Character.digit(ch, radix);
|
||||
} else {
|
||||
return parseAsciiDigit(ch, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse String as IPv4 address literal by following
|
||||
* BSD-style formatting rules.
|
||||
*
|
||||
* @param input input string
|
||||
* @return {@code true} if input string is parsable as IPv4 address literal,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isBsdParsableV4(String input) {
|
||||
char firstSymbol = input.charAt(0);
|
||||
// Check if first digit is not a decimal digit
|
||||
if (parseAsciiDigit(firstSymbol, DECIMAL) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Last character is dot OR is not a supported digit: [0-9,A-F,a-f]
|
||||
char lastSymbol = input.charAt(input.length() - 1);
|
||||
if (lastSymbol == '.' || parseAsciiHexDigit(lastSymbol) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse IP address fields
|
||||
CharBuffer charBuffer = CharBuffer.wrap(input);
|
||||
int fieldNumber = 0;
|
||||
while (charBuffer.hasRemaining()) {
|
||||
long fieldValue = -1L;
|
||||
// Try to parse fields in all supported radixes
|
||||
for (int radix : SUPPORTED_RADIXES) {
|
||||
fieldValue = parseV4FieldBsd(radix, charBuffer, fieldNumber);
|
||||
if (fieldValue >= 0) {
|
||||
fieldNumber++;
|
||||
break;
|
||||
} else if (fieldValue == TERMINAL_PARSE_ERROR) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If field can't be parsed as one of supported radixes stop
|
||||
// parsing
|
||||
if (fieldValue < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method tries to parse IP address field that starts from {@linkplain CharBuffer#position()
|
||||
* current position} of the provided character buffer.
|
||||
* <p>
|
||||
* This method supports three {@code "radix"} values to decode field values in
|
||||
* {@code "HEXADECIMAL (radix=16)"}, {@code "DECIMAL (radix=10)"} and
|
||||
* {@code "OCTAL (radix=8)"} radixes.
|
||||
* <p>
|
||||
* If {@code -1} value is returned the char buffer position is reset to the value
|
||||
* it was before it was called.
|
||||
* <p>
|
||||
* Method returns {@code -2} if formatting illegal for all supported {@code radix}
|
||||
* values is observed, and there is no point in checking other radix values.
|
||||
* That includes the following cases:<ul>
|
||||
* <li>Two subsequent dots are observer
|
||||
* <li>Number of dots more than 3
|
||||
* <li>Field value exceeds max allowed
|
||||
* <li>Character is not a valid digit for the requested {@code radix} value, given
|
||||
* that a field has the radix specific prefix
|
||||
* </ul>
|
||||
*
|
||||
* @param radix digits encoding radix to use for parsing. Valid values: 8, 10, 16.
|
||||
* @param buffer {@code CharBuffer} with position set to the field's fist character
|
||||
* @param fieldNumber parsed field number
|
||||
* @return {@code CANT_PARSE_IN_RADIX} if field can not be parsed in requested {@code radix}.
|
||||
* {@code TERMINAL_PARSE_ERROR} if field can't be parsed and the whole parse process should be terminated.
|
||||
* Parsed field value otherwise.
|
||||
*/
|
||||
private static long parseV4FieldBsd(int radix, CharBuffer buffer, int fieldNumber) {
|
||||
int initialPos = buffer.position();
|
||||
long val = 0;
|
||||
int digitsCount = 0;
|
||||
if (!checkPrefix(buffer, radix)) {
|
||||
val = CANT_PARSE_IN_RADIX;
|
||||
}
|
||||
boolean dotSeen = false;
|
||||
while (buffer.hasRemaining() && val != CANT_PARSE_IN_RADIX && !dotSeen) {
|
||||
char c = buffer.get();
|
||||
if (c == '.') {
|
||||
dotSeen = true;
|
||||
// Fail if 4 dots in IP address string.
|
||||
// fieldNumber counter starts from 0, therefore 3
|
||||
if (fieldNumber == 3) {
|
||||
// Terminal state, can stop parsing: too many fields
|
||||
return TERMINAL_PARSE_ERROR;
|
||||
}
|
||||
// Check for literals with two dots, like '1.2..3', '1.2.3..'
|
||||
if (digitsCount == 0) {
|
||||
// Terminal state, can stop parsing: dot with no digits
|
||||
return TERMINAL_PARSE_ERROR;
|
||||
}
|
||||
if (val > 255) {
|
||||
// Terminal state, can stop parsing: too big value for an octet
|
||||
return TERMINAL_PARSE_ERROR;
|
||||
}
|
||||
} else {
|
||||
int dv = parseAsciiDigit(c, radix);
|
||||
if (dv >= 0) {
|
||||
digitsCount++;
|
||||
val *= radix;
|
||||
val += dv;
|
||||
} else {
|
||||
// Spotted digit can't be parsed in the requested 'radix'.
|
||||
// The order in which radixes are checked - hex, octal, decimal:
|
||||
// - if symbol is not a valid digit in hex radix - terminal
|
||||
// - if symbol is not a valid digit in octal radix, and given
|
||||
// that octal prefix was observed before - terminal
|
||||
// - if symbol is not a valid digit in decimal radix - terminal
|
||||
return TERMINAL_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val == CANT_PARSE_IN_RADIX) {
|
||||
buffer.position(initialPos);
|
||||
} else if (!dotSeen) {
|
||||
// It is the last field - check its value
|
||||
// This check will ensure that address strings with less
|
||||
// than 4 fields, i.e. A, A.B and A.B.C address types
|
||||
// contain value less then the allowed maximum for the last field.
|
||||
long maxValue = (1L << ((4 - fieldNumber) * 8)) - 1;
|
||||
if (val > maxValue) {
|
||||
// Terminal state, can stop parsing: last field value exceeds its
|
||||
// allowed value
|
||||
return TERMINAL_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// This method moves the position of the supplied CharBuffer by analysing the digit prefix
|
||||
// symbols if any.
|
||||
// The caller should reset the position when method returns false.
|
||||
private static boolean checkPrefix(CharBuffer buffer, int radix) {
|
||||
switch (radix) {
|
||||
case OCTAL:
|
||||
return isOctalFieldStart(buffer);
|
||||
case DECIMAL:
|
||||
return isDecimalFieldStart(buffer);
|
||||
case HEXADECIMAL:
|
||||
return isHexFieldStart(buffer);
|
||||
default:
|
||||
throw new AssertionError("Not supported radix");
|
||||
}
|
||||
}
|
||||
|
||||
// This method always moves the position of the supplied CharBuffer
|
||||
// removing the octal prefix symbols '0'.
|
||||
// The caller should reset the position when method returns false.
|
||||
private static boolean isOctalFieldStart(CharBuffer cb) {
|
||||
// .0<EOS> is not treated as octal field
|
||||
if (cb.remaining() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch two first characters
|
||||
int position = cb.position();
|
||||
char first = cb.get();
|
||||
char second = cb.get();
|
||||
|
||||
// Return false if the first char is not octal prefix '0' or second is a
|
||||
// field separator - parseV4FieldBsd will reset position to start of the field.
|
||||
// '.0.' fields will be successfully parsed in decimal radix.
|
||||
boolean isOctalPrefix = first == '0' && second != '.';
|
||||
|
||||
// If the prefix looks like octal - consume '0', otherwise 'false' is returned
|
||||
// and caller will reset the buffer position.
|
||||
if (isOctalPrefix) {
|
||||
cb.position(position + 1);
|
||||
}
|
||||
return isOctalPrefix;
|
||||
}
|
||||
|
||||
// This method doesn't move the position of the supplied CharBuffer
|
||||
private static boolean isDecimalFieldStart(CharBuffer cb) {
|
||||
return cb.hasRemaining();
|
||||
}
|
||||
|
||||
// This method always moves the position of the supplied CharBuffer
|
||||
// removing the hexadecimal prefix symbols '0x'.
|
||||
// The caller should reset the position when method returns false.
|
||||
private static boolean isHexFieldStart(CharBuffer cb) {
|
||||
if (cb.remaining() < 2) {
|
||||
return false;
|
||||
}
|
||||
char first = cb.get();
|
||||
char second = cb.get();
|
||||
return first == '0' && (second == 'x' || second == 'X');
|
||||
}
|
||||
|
||||
// Parse ASCII digit in given radix
|
||||
private static int parseAsciiDigit(char c, int radix) {
|
||||
assert radix == OCTAL || radix == DECIMAL || radix == HEXADECIMAL;
|
||||
if (radix == HEXADECIMAL) {
|
||||
return parseAsciiHexDigit(c);
|
||||
}
|
||||
int val = c - '0';
|
||||
return (val < 0 || val >= radix) ? -1 : val;
|
||||
}
|
||||
|
||||
// Parse ASCII digit in hexadecimal radix
|
||||
private static int parseAsciiHexDigit(char digit) {
|
||||
char c = Character.toLowerCase(digit);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
return parseAsciiDigit(c, DECIMAL);
|
||||
}
|
||||
|
||||
// Supported radixes
|
||||
private static final int HEXADECIMAL = 16;
|
||||
private static final int DECIMAL = 10;
|
||||
private static final int OCTAL = 8;
|
||||
// Order in which field formats are exercised to parse one IP address textual field
|
||||
private static final int[] SUPPORTED_RADIXES = new int[]{HEXADECIMAL, OCTAL, DECIMAL};
|
||||
|
||||
// BSD parser's return values
|
||||
private final static long CANT_PARSE_IN_RADIX = -1L;
|
||||
private final static long TERMINAL_PARSE_ERROR = -2L;
|
||||
|
||||
private static final String ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP = "jdk.net.allowAmbiguousIPAddressLiterals";
|
||||
private static final boolean ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE = Boolean.valueOf(
|
||||
GetPropertyAction.privilegedGetProperty(ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP, "false"));
|
||||
}
|
||||
|
||||
@@ -63,22 +63,32 @@ public class BitArray {
|
||||
repn = new byte[(length + BITS_PER_UNIT - 1)/BITS_PER_UNIT];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BitArray of the specified size, initialized from the
|
||||
* specified byte array. The most significant bit of {@code a[0]} gets
|
||||
* index zero in the BitArray. The array must be large enough to specify
|
||||
* a value for every bit of the BitArray. i.e. {@code 8*a.length <= length}.
|
||||
*/
|
||||
public BitArray(int length, byte[] a) throws IllegalArgumentException {
|
||||
this(length, a, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BitArray of the specified size, initialized from the
|
||||
* specified byte array. The most significant bit of {@code a[0]} gets
|
||||
* index zero in the BitArray. The array a must be large enough
|
||||
* to specify a value for every bit in the BitArray. In other words,
|
||||
* {@code 8*a.length <= length}.
|
||||
* specified byte array starting at the specified offset. The most
|
||||
* significant bit of {@code a[ofs]} gets index zero in the BitArray.
|
||||
* The array must be large enough to specify a value for every bit of
|
||||
* the BitArray, i.e. {@code 8*(a.length - ofs) <= length}.
|
||||
*/
|
||||
public BitArray(int length, byte[] a) throws IllegalArgumentException {
|
||||
public BitArray(int length, byte[] a, int ofs)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length for BitArray");
|
||||
}
|
||||
if (a.length * BITS_PER_UNIT < length) {
|
||||
throw new IllegalArgumentException("Byte array too short to represent " +
|
||||
"bit array of given length");
|
||||
if ((a.length - ofs) * BITS_PER_UNIT < length) {
|
||||
throw new IllegalArgumentException
|
||||
("Byte array too short to represent " + length + "-bit array");
|
||||
}
|
||||
|
||||
this.length = length;
|
||||
@@ -93,7 +103,7 @@ public class BitArray {
|
||||
2. zero out extra bits in the last byte
|
||||
*/
|
||||
repn = new byte[repLength];
|
||||
System.arraycopy(a, 0, repn, 0, repLength);
|
||||
System.arraycopy(a, ofs, repn, 0, repLength);
|
||||
if (repLength > 0) {
|
||||
repn[repLength - 1] &= bitMask;
|
||||
}
|
||||
@@ -266,7 +276,7 @@ public class BitArray {
|
||||
public BitArray truncate() {
|
||||
for (int i=length-1; i>=0; i--) {
|
||||
if (get(i)) {
|
||||
return new BitArray(i+1, Arrays.copyOf(repn, (i + BITS_PER_UNIT)/BITS_PER_UNIT));
|
||||
return new BitArray(i+1, repn, 0);
|
||||
}
|
||||
}
|
||||
return new BitArray(1);
|
||||
|
||||
@@ -189,6 +189,28 @@ class DerInputBuffer extends ByteArrayInputStream implements Cloneable {
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
// check the number of pad bits, validate the pad bits in the bytes
|
||||
// if enforcing DER (i.e. allowBER == false), and return the number of
|
||||
// bits of the resulting BitString
|
||||
private static int checkPaddedBits(int numOfPadBits, byte[] data, int start,
|
||||
int end, boolean allowBER) throws IOException {
|
||||
// number of pad bits should be from 0(min) to 7(max).
|
||||
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
|
||||
throw new IOException("Invalid number of padding bits");
|
||||
}
|
||||
int lenInBits = ((end - start) << 3) - numOfPadBits;
|
||||
if (lenInBits < 0) {
|
||||
throw new IOException("Not enough bytes in BitString");
|
||||
}
|
||||
|
||||
// padding bits should be all zeros for DER
|
||||
if (!allowBER && numOfPadBits != 0 &&
|
||||
(data[end - 1] & (0xff >>> (8 - numOfPadBits))) != 0) {
|
||||
throw new IOException("Invalid value of padding bits");
|
||||
}
|
||||
return lenInBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bit string which takes up the specified
|
||||
* number of bytes in this buffer.
|
||||
@@ -201,18 +223,20 @@ class DerInputBuffer extends ByteArrayInputStream implements Cloneable {
|
||||
throw new IOException("Invalid encoding: zero length bit string");
|
||||
}
|
||||
|
||||
int numOfPadBits = buf[pos];
|
||||
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
|
||||
throw new IOException("Invalid number of padding bits");
|
||||
}
|
||||
int start = pos;
|
||||
int end = start + len;
|
||||
skip(len); // Compatibility.
|
||||
|
||||
int numOfPadBits = buf[start++];
|
||||
checkPaddedBits(numOfPadBits, buf, start, end, allowBER);
|
||||
|
||||
// minus the first byte which indicates the number of padding bits
|
||||
byte[] retval = new byte[len - 1];
|
||||
System.arraycopy(buf, pos + 1, retval, 0, len - 1);
|
||||
if (numOfPadBits != 0) {
|
||||
// get rid of the padding bits
|
||||
retval[len - 2] &= (0xff << numOfPadBits);
|
||||
System.arraycopy(buf, start, retval, 0, len - 1);
|
||||
if (allowBER && numOfPadBits != 0) {
|
||||
// fix the potential non-zero padding bits
|
||||
retval[retval.length - 1] &= (0xff << numOfPadBits);
|
||||
}
|
||||
skip(len);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -228,26 +252,35 @@ class DerInputBuffer extends ByteArrayInputStream implements Cloneable {
|
||||
* The bit string need not be byte-aligned.
|
||||
*/
|
||||
BitArray getUnalignedBitString() throws IOException {
|
||||
return getUnalignedBitString(available());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bit string which takes up the specified
|
||||
* number of bytes in this buffer.
|
||||
* The bit string need not be byte-aligned.
|
||||
*/
|
||||
BitArray getUnalignedBitString(int len) throws IOException {
|
||||
if (len > available())
|
||||
throw new IOException("short read of bit string");
|
||||
|
||||
if (len == 0) {
|
||||
throw new IOException("Invalid encoding: zero length bit string");
|
||||
}
|
||||
|
||||
if (pos >= count)
|
||||
return null;
|
||||
/*
|
||||
* Just copy the data into an aligned, padded octet buffer,
|
||||
* and consume the rest of the buffer.
|
||||
*/
|
||||
int len = available();
|
||||
int unusedBits = buf[pos] & 0xff;
|
||||
if (unusedBits > 7 ) {
|
||||
throw new IOException("Invalid value for unused bits: " + unusedBits);
|
||||
}
|
||||
byte[] bits = new byte[len - 1];
|
||||
// number of valid bits
|
||||
int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits;
|
||||
|
||||
System.arraycopy(buf, pos + 1, bits, 0, len - 1);
|
||||
|
||||
BitArray bitArray = new BitArray(length, bits);
|
||||
pos = count;
|
||||
return bitArray;
|
||||
int start = pos;
|
||||
int end = start + len;
|
||||
pos = count; // Compatibility.
|
||||
int numOfPadBits = buf[start++];
|
||||
int lenInBits = checkPaddedBits(numOfPadBits, buf, start,
|
||||
end, allowBER);
|
||||
return new BitArray(lenInBits, buf, start);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -261,27 +261,7 @@ public class DerInputStream {
|
||||
return new BitArray(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* First byte = number of excess bits in the last octet of the
|
||||
* representation.
|
||||
*/
|
||||
length--;
|
||||
int excessBits = buffer.read();
|
||||
if (excessBits < 0) {
|
||||
throw new IOException("Unused bits of bit string invalid");
|
||||
}
|
||||
int validBits = length*8 - excessBits;
|
||||
if (validBits < 0) {
|
||||
throw new IOException("Valid bits of bit string invalid");
|
||||
}
|
||||
|
||||
byte[] repn = new byte[length];
|
||||
|
||||
if ((length != 0) && (buffer.read(repn) != length)) {
|
||||
throw new IOException("Short read of DER bit string");
|
||||
}
|
||||
|
||||
return new BitArray(validBits, repn);
|
||||
return buffer.getUnalignedBitString(length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -276,11 +276,6 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
|
||||
int wrap = 1;
|
||||
static const char my_version[] = ZLIB_VERSION;
|
||||
|
||||
ushf *overlay;
|
||||
/* We overlay pending_buf and d_buf+l_buf. This works since the average
|
||||
* output size for (length,distance) codes is <= 24 bits.
|
||||
*/
|
||||
|
||||
if (version == Z_NULL || version[0] != my_version[0] ||
|
||||
stream_size != sizeof(z_stream)) {
|
||||
return Z_VERSION_ERROR;
|
||||
@@ -350,9 +345,47 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
|
||||
|
||||
s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
|
||||
|
||||
overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
|
||||
s->pending_buf = (uchf *) overlay;
|
||||
s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
|
||||
/* We overlay pending_buf and sym_buf. This works since the average size
|
||||
* for length/distance pairs over any compressed block is assured to be 31
|
||||
* bits or less.
|
||||
*
|
||||
* Analysis: The longest fixed codes are a length code of 8 bits plus 5
|
||||
* extra bits, for lengths 131 to 257. The longest fixed distance codes are
|
||||
* 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest
|
||||
* possible fixed-codes length/distance pair is then 31 bits total.
|
||||
*
|
||||
* sym_buf starts one-fourth of the way into pending_buf. So there are
|
||||
* three bytes in sym_buf for every four bytes in pending_buf. Each symbol
|
||||
* in sym_buf is three bytes -- two for the distance and one for the
|
||||
* literal/length. As each symbol is consumed, the pointer to the next
|
||||
* sym_buf value to read moves forward three bytes. From that symbol, up to
|
||||
* 31 bits are written to pending_buf. The closest the written pending_buf
|
||||
* bits gets to the next sym_buf symbol to read is just before the last
|
||||
* code is written. At that time, 31*(n-2) bits have been written, just
|
||||
* after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
|
||||
* 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
|
||||
* symbols are written.) The closest the writing gets to what is unread is
|
||||
* then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
|
||||
* can range from 128 to 32768.
|
||||
*
|
||||
* Therefore, at a minimum, there are 142 bits of space between what is
|
||||
* written and what is read in the overlain buffers, so the symbols cannot
|
||||
* be overwritten by the compressed data. That space is actually 139 bits,
|
||||
* due to the three-bit fixed-code block header.
|
||||
*
|
||||
* That covers the case where either Z_FIXED is specified, forcing fixed
|
||||
* codes, or when the use of fixed codes is chosen, because that choice
|
||||
* results in a smaller compressed block than dynamic codes. That latter
|
||||
* condition then assures that the above analysis also covers all dynamic
|
||||
* blocks. A dynamic-code block will only be chosen to be emitted if it has
|
||||
* fewer bits than a fixed-code block would for the same set of symbols.
|
||||
* Therefore its average symbol length is assured to be less than 31. So
|
||||
* the compressed data for a dynamic block also cannot overwrite the
|
||||
* symbols from which it is being constructed.
|
||||
*/
|
||||
|
||||
s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4);
|
||||
s->pending_buf_size = (ulg)s->lit_bufsize * 4;
|
||||
|
||||
if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
|
||||
s->pending_buf == Z_NULL) {
|
||||
@@ -361,8 +394,12 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
|
||||
deflateEnd (strm);
|
||||
return Z_MEM_ERROR;
|
||||
}
|
||||
s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
|
||||
s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
|
||||
s->sym_buf = s->pending_buf + s->lit_bufsize;
|
||||
s->sym_end = (s->lit_bufsize - 1) * 3;
|
||||
/* We avoid equality with lit_bufsize*3 because of wraparound at 64K
|
||||
* on 16 bit machines and because stored blocks are restricted to
|
||||
* 64K-1 bytes.
|
||||
*/
|
||||
|
||||
s->level = level;
|
||||
s->strategy = strategy;
|
||||
@@ -573,7 +610,8 @@ int ZEXPORT deflatePrime (strm, bits, value)
|
||||
|
||||
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
|
||||
s = strm->state;
|
||||
if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
|
||||
if (bits < 0 || bits > 16 ||
|
||||
s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
|
||||
return Z_BUF_ERROR;
|
||||
do {
|
||||
put = Buf_size - s->bi_valid;
|
||||
@@ -1132,7 +1170,6 @@ int ZEXPORT deflateCopy (dest, source)
|
||||
#else
|
||||
deflate_state *ds;
|
||||
deflate_state *ss;
|
||||
ushf *overlay;
|
||||
|
||||
|
||||
if (deflateStateCheck(source) || dest == Z_NULL) {
|
||||
@@ -1152,8 +1189,7 @@ int ZEXPORT deflateCopy (dest, source)
|
||||
ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
|
||||
ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
|
||||
ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
|
||||
overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
|
||||
ds->pending_buf = (uchf *) overlay;
|
||||
ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4);
|
||||
|
||||
if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
|
||||
ds->pending_buf == Z_NULL) {
|
||||
@@ -1167,8 +1203,7 @@ int ZEXPORT deflateCopy (dest, source)
|
||||
zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
|
||||
|
||||
ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
|
||||
ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
|
||||
ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
|
||||
ds->sym_buf = ds->pending_buf + ds->lit_bufsize;
|
||||
|
||||
ds->l_desc.dyn_tree = ds->dyn_ltree;
|
||||
ds->d_desc.dyn_tree = ds->dyn_dtree;
|
||||
@@ -1936,7 +1971,7 @@ local block_state deflate_fast(s, flush)
|
||||
FLUSH_BLOCK(s, 1);
|
||||
return finish_done;
|
||||
}
|
||||
if (s->last_lit)
|
||||
if (s->sym_next)
|
||||
FLUSH_BLOCK(s, 0);
|
||||
return block_done;
|
||||
}
|
||||
@@ -2067,7 +2102,7 @@ local block_state deflate_slow(s, flush)
|
||||
FLUSH_BLOCK(s, 1);
|
||||
return finish_done;
|
||||
}
|
||||
if (s->last_lit)
|
||||
if (s->sym_next)
|
||||
FLUSH_BLOCK(s, 0);
|
||||
return block_done;
|
||||
}
|
||||
@@ -2142,7 +2177,7 @@ local block_state deflate_rle(s, flush)
|
||||
FLUSH_BLOCK(s, 1);
|
||||
return finish_done;
|
||||
}
|
||||
if (s->last_lit)
|
||||
if (s->sym_next)
|
||||
FLUSH_BLOCK(s, 0);
|
||||
return block_done;
|
||||
}
|
||||
@@ -2181,7 +2216,7 @@ local block_state deflate_huff(s, flush)
|
||||
FLUSH_BLOCK(s, 1);
|
||||
return finish_done;
|
||||
}
|
||||
if (s->last_lit)
|
||||
if (s->sym_next)
|
||||
FLUSH_BLOCK(s, 0);
|
||||
return block_done;
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ typedef struct internal_state {
|
||||
/* Depth of each subtree used as tie breaker for trees of equal frequency
|
||||
*/
|
||||
|
||||
uchf *l_buf; /* buffer for literals or lengths */
|
||||
uchf *sym_buf; /* buffer for distances and literals/lengths */
|
||||
|
||||
uInt lit_bufsize;
|
||||
/* Size of match buffer for literals/lengths. There are 4 reasons for
|
||||
@@ -263,13 +263,8 @@ typedef struct internal_state {
|
||||
* - I can't count above 4
|
||||
*/
|
||||
|
||||
uInt last_lit; /* running index in l_buf */
|
||||
|
||||
ushf *d_buf;
|
||||
/* Buffer for distances. To simplify the code, d_buf and l_buf have
|
||||
* the same number of elements. To use different lengths, an extra flag
|
||||
* array would be necessary.
|
||||
*/
|
||||
uInt sym_next; /* running index in sym_buf */
|
||||
uInt sym_end; /* symbol table full when sym_next reaches this */
|
||||
|
||||
ulg opt_len; /* bit length of current block with optimal trees */
|
||||
ulg static_len; /* bit length of current block with static trees */
|
||||
@@ -349,20 +344,22 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
|
||||
|
||||
# define _tr_tally_lit(s, c, flush) \
|
||||
{ uch cc = (c); \
|
||||
s->d_buf[s->last_lit] = 0; \
|
||||
s->l_buf[s->last_lit++] = cc; \
|
||||
s->sym_buf[s->sym_next++] = 0; \
|
||||
s->sym_buf[s->sym_next++] = 0; \
|
||||
s->sym_buf[s->sym_next++] = cc; \
|
||||
s->dyn_ltree[cc].Freq++; \
|
||||
flush = (s->last_lit == s->lit_bufsize-1); \
|
||||
flush = (s->sym_next == s->sym_end); \
|
||||
}
|
||||
# define _tr_tally_dist(s, distance, length, flush) \
|
||||
{ uch len = (uch)(length); \
|
||||
ush dist = (ush)(distance); \
|
||||
s->d_buf[s->last_lit] = dist; \
|
||||
s->l_buf[s->last_lit++] = len; \
|
||||
s->sym_buf[s->sym_next++] = (uch)dist; \
|
||||
s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \
|
||||
s->sym_buf[s->sym_next++] = len; \
|
||||
dist--; \
|
||||
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
|
||||
s->dyn_dtree[d_code(dist)].Freq++; \
|
||||
flush = (s->last_lit == s->lit_bufsize-1); \
|
||||
flush = (s->sym_next == s->sym_end); \
|
||||
}
|
||||
#else
|
||||
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
|
||||
|
||||
@@ -440,7 +440,7 @@ local void init_block(s)
|
||||
|
||||
s->dyn_ltree[END_BLOCK].Freq = 1;
|
||||
s->opt_len = s->static_len = 0L;
|
||||
s->last_lit = s->matches = 0;
|
||||
s->sym_next = s->matches = 0;
|
||||
}
|
||||
|
||||
#define SMALLEST 1
|
||||
@@ -971,7 +971,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
|
||||
|
||||
Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
|
||||
opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
|
||||
s->last_lit));
|
||||
s->sym_next / 3));
|
||||
|
||||
if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
|
||||
|
||||
@@ -1040,8 +1040,9 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
|
||||
unsigned dist; /* distance of matched string */
|
||||
unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
|
||||
{
|
||||
s->d_buf[s->last_lit] = (ush)dist;
|
||||
s->l_buf[s->last_lit++] = (uch)lc;
|
||||
s->sym_buf[s->sym_next++] = (uch)dist;
|
||||
s->sym_buf[s->sym_next++] = (uch)(dist >> 8);
|
||||
s->sym_buf[s->sym_next++] = (uch)lc;
|
||||
if (dist == 0) {
|
||||
/* lc is the unmatched char */
|
||||
s->dyn_ltree[lc].Freq++;
|
||||
@@ -1056,30 +1057,7 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
|
||||
s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
|
||||
s->dyn_dtree[d_code(dist)].Freq++;
|
||||
}
|
||||
|
||||
#ifdef TRUNCATE_BLOCK
|
||||
/* Try to guess if it is profitable to stop the current block here */
|
||||
if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
|
||||
/* Compute an upper bound for the compressed length */
|
||||
ulg out_length = (ulg)s->last_lit*8L;
|
||||
ulg in_length = (ulg)((long)s->strstart - s->block_start);
|
||||
int dcode;
|
||||
for (dcode = 0; dcode < D_CODES; dcode++) {
|
||||
out_length += (ulg)s->dyn_dtree[dcode].Freq *
|
||||
(5L+extra_dbits[dcode]);
|
||||
}
|
||||
out_length >>= 3;
|
||||
Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
|
||||
s->last_lit, in_length, out_length,
|
||||
100L - out_length*100L/in_length));
|
||||
if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
|
||||
}
|
||||
#endif
|
||||
return (s->last_lit == s->lit_bufsize-1);
|
||||
/* We avoid equality with lit_bufsize because of wraparound at 64K
|
||||
* on 16 bit machines and because stored blocks are restricted to
|
||||
* 64K-1 bytes.
|
||||
*/
|
||||
return (s->sym_next == s->sym_end);
|
||||
}
|
||||
|
||||
/* ===========================================================================
|
||||
@@ -1092,13 +1070,14 @@ local void compress_block(s, ltree, dtree)
|
||||
{
|
||||
unsigned dist; /* distance of matched string */
|
||||
int lc; /* match length or unmatched char (if dist == 0) */
|
||||
unsigned lx = 0; /* running index in l_buf */
|
||||
unsigned sx = 0; /* running index in sym_buf */
|
||||
unsigned code; /* the code to send */
|
||||
int extra; /* number of extra bits to send */
|
||||
|
||||
if (s->last_lit != 0) do {
|
||||
dist = s->d_buf[lx];
|
||||
lc = s->l_buf[lx++];
|
||||
if (s->sym_next != 0) do {
|
||||
dist = s->sym_buf[sx++] & 0xff;
|
||||
dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
|
||||
lc = s->sym_buf[sx++];
|
||||
if (dist == 0) {
|
||||
send_code(s, lc, ltree); /* send a literal byte */
|
||||
Tracecv(isgraph(lc), (stderr," '%c' ", lc));
|
||||
@@ -1123,11 +1102,10 @@ local void compress_block(s, ltree, dtree)
|
||||
}
|
||||
} /* literal or match pair ? */
|
||||
|
||||
/* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
|
||||
Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
|
||||
"pendingBuf overflow");
|
||||
/* Check that the overlay between pending_buf and sym_buf is ok: */
|
||||
Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
|
||||
|
||||
} while (lx < s->last_lit);
|
||||
} while (sx < s->sym_next);
|
||||
|
||||
send_code(s, END_BLOCK, ltree);
|
||||
}
|
||||
|
||||
6
src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java
Normal file → Executable file
6
src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java
Normal file → Executable file
@@ -179,6 +179,12 @@ class WindowsDirectoryStream
|
||||
int nextEntryOffset = WindowsFileAttributes.getNextOffsetFromFileDirInformation(
|
||||
queryDirectoryInformation, dirInformationAddress);
|
||||
nextOffset = nextEntryOffset == 0 ? -1 : nextOffset + nextEntryOffset;
|
||||
if (nextOffset > NATIVE_BUFFER_SIZE - WindowsFileAttributes.SIZEOF_FILE_DIRECTORY_INFORMATION) {
|
||||
// The offset to the next data structure sometimes points past the end of the buffer.
|
||||
// Treat this as there are no more data (and hope NextNtQueryDirectoryInformation()
|
||||
// didn't write past the allocated memory).
|
||||
nextOffset = -1;
|
||||
}
|
||||
name = WindowsFileAttributes.getFileNameFromFileDirInformation(
|
||||
queryDirectoryInformation, dirInformationAddress);
|
||||
if (isSelfOrParent(name)) {
|
||||
|
||||
@@ -160,6 +160,9 @@ class WindowsFileAttributes
|
||||
private static final int OFFSETOF_DIR_INFO_FILENAME_LENGTH = 60;
|
||||
private static final int OFFSETOF_DIR_INFO_FILENAME = 64;
|
||||
|
||||
// Minimum sane size of either _FILE_DIRECTORY_INFORMATION or _FILE_ID_FULL_DIR_INFORMATION
|
||||
public static final int SIZEOF_FILE_DIRECTORY_INFORMATION = 65;
|
||||
|
||||
// used to adjust values between Windows and java epoch
|
||||
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
|
||||
|
||||
|
||||
@@ -32,22 +32,36 @@ public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
|
||||
super(compFont);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int charToVariationGlyph(int unicode, int variationSelector) {
|
||||
if (variationSelector == 0) return charToGlyph(unicode);
|
||||
else {
|
||||
int glyph = convertToGlyph(unicode, variationSelector);
|
||||
if (glyph == missingGlyph) glyph = charToGlyph(unicode);
|
||||
return glyph;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int convertToGlyph(int unicode) {
|
||||
int glyph = convertToGlyph(unicode, 0);
|
||||
setCachedGlyphCode(unicode, glyph);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int convertToGlyph(int unicode, int variationSelector) {
|
||||
CCompositeFont compositeFont = (CCompositeFont) font;
|
||||
CFont mainFont = (CFont) font.getSlotFont(0);
|
||||
String[] fallbackFontInfo = new String[2];
|
||||
int glyphCode = nativeCodePointToGlyph(mainFont.getNativeFontPtr(), unicode, fallbackFontInfo);
|
||||
int glyphCode = nativeCodePointToGlyph(mainFont.getNativeFontPtr(), unicode, variationSelector, fallbackFontInfo);
|
||||
if (glyphCode == missingGlyph) {
|
||||
setCachedGlyphCode(unicode, missingGlyph);
|
||||
return missingGlyph;
|
||||
}
|
||||
String fallbackFontName = fallbackFontInfo[0];
|
||||
String fallbackFontFamilyName = fallbackFontInfo[1];
|
||||
if (fallbackFontName == null || fallbackFontFamilyName == null) {
|
||||
int result = compositeGlyphCode(0, glyphCode);
|
||||
setCachedGlyphCode(unicode, result);
|
||||
return result;
|
||||
return compositeGlyphCode(0, glyphCode);
|
||||
}
|
||||
|
||||
int slot = compositeFont.findSlot(fallbackFontName);
|
||||
@@ -68,12 +82,10 @@ public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
|
||||
slot = compositeFont.addSlot((CFont) fallbackFont);
|
||||
}
|
||||
|
||||
int result = compositeGlyphCode(slot, glyphCode);
|
||||
setCachedGlyphCode(unicode, result);
|
||||
return result;
|
||||
return compositeGlyphCode(slot, glyphCode);
|
||||
}
|
||||
|
||||
// This invokes native font fallback mechanism, returning information about font (its Postscript and family names)
|
||||
// able to display a given character, and corresponding glyph code
|
||||
private static native int nativeCodePointToGlyph(long nativeFontPtr, int codePoint, String[] result);
|
||||
private static native int nativeCodePointToGlyph(long nativeFontPtr, int codePoint, int variationSelector, String[] result);
|
||||
}
|
||||
@@ -67,6 +67,12 @@ public final class CStrike extends PhysicalStrike {
|
||||
double x,
|
||||
double y);
|
||||
|
||||
private static native void getNativeGlyphRenderData(long nativeStrikePtr,
|
||||
int glyphCode,
|
||||
double x,
|
||||
double y,
|
||||
GlyphRenderData result);
|
||||
|
||||
private static native void getNativeGlyphOutlineBounds(long nativeStrikePtr,
|
||||
int glyphCode,
|
||||
float [] rectData);
|
||||
@@ -232,6 +238,12 @@ public final class CStrike extends PhysicalStrike {
|
||||
throw new Error("not implemented yet");
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
GlyphRenderData result = new GlyphRenderData();
|
||||
getNativeGlyphRenderData(getNativeStrikePtr(), glyphCode, x, y, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// called from the Sun2D renderer
|
||||
long getGlyphImagePtr(int glyphCode) {
|
||||
synchronized (glyphInfoCache) {
|
||||
|
||||
@@ -83,4 +83,8 @@ public class NativeStrike extends PhysicalStrike {
|
||||
return null;
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -968,7 +968,7 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean res = parentPeer.requestWindowFocus(cause);
|
||||
boolean res = !focusedWindowChangeAllowed || parentPeer.requestWindowFocus(cause);
|
||||
// If parent window can be made focused and has been made focused (synchronously)
|
||||
// then we can proceed with children, otherwise we retreat
|
||||
if (!res || !parentWindow.isFocused()) {
|
||||
|
||||
@@ -271,6 +271,7 @@ public class CInputMethod extends InputMethodAdapter {
|
||||
nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
|
||||
}
|
||||
|
||||
fAwtFocussedComponent = null;
|
||||
fAwtFocussedComponentPeer = null;
|
||||
}
|
||||
|
||||
@@ -281,34 +282,32 @@ public class CInputMethod extends InputMethodAdapter {
|
||||
* to talk to when responding to key events.
|
||||
*/
|
||||
protected void setAWTFocussedComponent(Component component) {
|
||||
LWComponentPeer<?, ?> peer = null;
|
||||
long modelPtr = 0;
|
||||
CInputMethod imInstance = this;
|
||||
if (component == null || component == fAwtFocussedComponent) {
|
||||
// Sometimes input happens for the natively unfocused window
|
||||
// (e.g. in case of system emoji picker),
|
||||
// so we don't reset last focused component on focus lost.
|
||||
return;
|
||||
}
|
||||
|
||||
// component will be null when we are told there's no focused component.
|
||||
// When that happens we need to notify the native architecture to stop generating IMEs
|
||||
if (component == null) {
|
||||
peer = fAwtFocussedComponentPeer;
|
||||
imInstance = null;
|
||||
} else {
|
||||
peer = getNearestNativePeer(component);
|
||||
if (fAwtFocussedComponentPeer != null) {
|
||||
long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
|
||||
nativeNotifyPeer(modelPtr, null);
|
||||
}
|
||||
|
||||
fAwtFocussedComponent = component;
|
||||
fAwtFocussedComponentPeer = getNearestNativePeer(component);
|
||||
|
||||
if (fAwtFocussedComponentPeer != null) {
|
||||
long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
|
||||
|
||||
CInputMethod imInstance = this;
|
||||
// If we have a passive client, don't pass input method events to it.
|
||||
if (component.getInputMethodRequests() == null) {
|
||||
imInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (peer != null) {
|
||||
modelPtr = getNativeViewPtr(peer);
|
||||
|
||||
// modelPtr refers to the ControlModel that either got or lost focus.
|
||||
nativeNotifyPeer(modelPtr, imInstance);
|
||||
}
|
||||
|
||||
// Track the focused component and its nearest peer.
|
||||
fAwtFocussedComponent = component;
|
||||
fAwtFocussedComponentPeer = getNearestNativePeer(component);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.awt.Insets;
|
||||
|
||||
import sun.lwawt.PlatformComponent;
|
||||
import sun.lwawt.PlatformWindow;
|
||||
import sun.lwawt.LWWindowPeer;
|
||||
|
||||
/**
|
||||
* On OSX {@code CPlatformComponent} stores pointer to the native CAlayer which
|
||||
@@ -63,7 +64,8 @@ class CPlatformComponent extends CFRetainedResource
|
||||
public void setBounds(final int x, final int y, final int w, final int h) {
|
||||
// translates values from the coordinate system of the top-level window
|
||||
// to the coordinate system of the content view
|
||||
final Insets insets = platformWindow.getPeer().getInsets();
|
||||
final LWWindowPeer peer = platformWindow.getPeer();
|
||||
final Insets insets = (peer != null) ? peer.getInsets() : new Insets(0, 0, 0, 0);
|
||||
execute(ptr->nativeSetBounds(ptr, x - insets.left, y - insets.top, w, h));
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,10 @@ final class CPlatformResponder {
|
||||
}
|
||||
|
||||
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
|
||||
if ((jeventType == MouseEvent.MOUSE_PRESSED) && (jbuttonNumber > MouseEvent.NOBUTTON)) {
|
||||
jmodifiers |= MouseEvent.getMaskForButton(jbuttonNumber);
|
||||
}
|
||||
|
||||
boolean jpopupTrigger = NSEvent.isPopupTrigger(jmodifiers);
|
||||
|
||||
eventNotifier.notifyMouseEvent(jeventType, System.currentTimeMillis(), jbuttonNumber,
|
||||
|
||||
@@ -338,15 +338,40 @@ static NSDictionary* prebuiltFamilyNames() {
|
||||
@".NewYork-Black" : @".New York",
|
||||
@".NewYork-BlackItalic" : @".New York",
|
||||
@".NewYork-Bold" : @".New York",
|
||||
@".NewYork-BoldG1" : @".New York",
|
||||
@".NewYork-BoldG2" : @".New York",
|
||||
@".NewYork-BoldG3" : @".New York",
|
||||
@".NewYork-BoldG4" : @".New York",
|
||||
@".NewYork-BoldItalic" : @".New York",
|
||||
@".NewYork-BoldItalicG1" : @".New York",
|
||||
@".NewYork-BoldItalicG2" : @".New York",
|
||||
@".NewYork-BoldItalicG3" : @".New York",
|
||||
@".NewYork-BoldItalicG4" : @".New York",
|
||||
@".NewYork-Heavy" : @".New York",
|
||||
@".NewYork-HeavyItalic" : @".New York",
|
||||
@".NewYork-Medium" : @".New York",
|
||||
@".NewYork-MediumItalic" : @".New York",
|
||||
@".NewYork-Regular" : @".New York",
|
||||
@".NewYork-RegularG1" : @".New York",
|
||||
@".NewYork-RegularG2" : @".New York",
|
||||
@".NewYork-RegularG3" : @".New York",
|
||||
@".NewYork-RegularG4" : @".New York",
|
||||
@".NewYork-RegularItalic" : @".New York",
|
||||
@".NewYork-RegularItalicG1" : @".New York",
|
||||
@".NewYork-RegularItalicG2" : @".New York",
|
||||
@".NewYork-RegularItalicG3" : @".New York",
|
||||
@".NewYork-RegularItalicG4" : @".New York",
|
||||
@".NewYork-Semibold" : @".New York",
|
||||
@".NewYork-SemiboldItalic" : @".New York",
|
||||
@".SFArabic-Black" : @".SF Arabic",
|
||||
@".SFArabic-Bold" : @".SF Arabic",
|
||||
@".SFArabic-Heavy" : @".SF Arabic",
|
||||
@".SFArabic-Light" : @".SF Arabic",
|
||||
@".SFArabic-Medium" : @".SF Arabic",
|
||||
@".SFArabic-Regular" : @".SF Arabic",
|
||||
@".SFArabic-Semibold" : @".SF Arabic",
|
||||
@".SFArabic-Thin" : @".SF Arabic",
|
||||
@".SFArabic-Ultralight" : @".SF Arabic",
|
||||
@".NotoNastaliqUrduUI" : @".Noto Nastaliq Urdu UI",
|
||||
@".NotoNastaliqUrduUI-Bold" : @".Noto Nastaliq Urdu UI",
|
||||
@".PingFangHK-Light" : @".PingFang HK",
|
||||
@@ -1701,6 +1726,43 @@ static NSDictionary* prebuiltFamilyNames() {
|
||||
|
||||
static NSDictionary* prebuiltFaceNames() {
|
||||
return @{
|
||||
@".NewYork-Black" : @"Black",
|
||||
@".NewYork-BlackItalic" : @"Black Italic",
|
||||
@".NewYork-Bold" : @"Bold",
|
||||
@".NewYork-BoldG1" : @"Bold G1",
|
||||
@".NewYork-BoldG2" : @"Bold G2",
|
||||
@".NewYork-BoldG3" : @"Bold G3",
|
||||
@".NewYork-BoldG4" : @"Bold G4",
|
||||
@".NewYork-BoldItalic" : @"Bold Italic",
|
||||
@".NewYork-BoldItalicG1" : @"Bold Italic G1",
|
||||
@".NewYork-BoldItalicG2" : @"Bold Italic G2",
|
||||
@".NewYork-BoldItalicG3" : @"Bold Italic G3",
|
||||
@".NewYork-BoldItalicG4" : @"Bold Italic G4",
|
||||
@".NewYork-Heavy" : @"Heavy",
|
||||
@".NewYork-HeavyItalic" : @"Heavy Italic",
|
||||
@".NewYork-Medium" : @"Medium",
|
||||
@".NewYork-MediumItalic" : @"Medium Italic",
|
||||
@".NewYork-Regular" : @"Regular",
|
||||
@".NewYork-RegularG1" : @"Regular G1",
|
||||
@".NewYork-RegularG2" : @"Regular G2",
|
||||
@".NewYork-RegularG3" : @"Regular G3",
|
||||
@".NewYork-RegularG4" : @"Regular G4",
|
||||
@".NewYork-RegularItalic" : @"Regular Italic",
|
||||
@".NewYork-RegularItalicG1" : @"Regular Italic G1",
|
||||
@".NewYork-RegularItalicG2" : @"Regular Italic G2",
|
||||
@".NewYork-RegularItalicG3" : @"Regular Italic G3",
|
||||
@".NewYork-RegularItalicG4" : @"Regular Italic G4",
|
||||
@".NewYork-Semibold" : @"Semibold",
|
||||
@".NewYork-SemiboldItalic" : @"Semibold Italic",
|
||||
@".SFArabic-Black" : @"Black",
|
||||
@".SFArabic-Bold" : @"Bold",
|
||||
@".SFArabic-Heavy" : @"Heavy",
|
||||
@".SFArabic-Light" : @"Light",
|
||||
@".SFArabic-Medium" : @"Medium",
|
||||
@".SFArabic-Regular" : @"Regular",
|
||||
@".SFArabic-Semibold" : @"Semibold",
|
||||
@".SFArabic-Thin" : @"Thin",
|
||||
@".SFArabic-Ultralight" : @"Ultralight",
|
||||
@".SFNS-Black" : @"Black",
|
||||
@".SFNS-BlackItalic" : @"Black Italic",
|
||||
@".SFNS-Bold" : @"Bold",
|
||||
@@ -3196,6 +3258,9 @@ JNI_COCOA_ENTER(env);
|
||||
CTFontRef ctfont = (CTFontRef)nsFont;
|
||||
CFArrayRef tagsArray =
|
||||
CTFontCopyAvailableTables(ctfont, kCTFontTableOptionNoOptions);
|
||||
if (tagsArray == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
CFIndex numTags = CFArrayGetCount(tagsArray);
|
||||
for (i=0; i<numTags; i++) {
|
||||
if (tag ==
|
||||
|
||||
@@ -164,19 +164,7 @@ JNI_COCOA_ENTER(env);
|
||||
// to indicate we should use CoreText to substitute the character
|
||||
CGGlyph glyph;
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
|
||||
if (IS_OSX_GT10_13 || CGGI_IsColorFont(cgFallback)) {
|
||||
CGAffineTransform matrix = awtStrike->fAltTx;
|
||||
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
|
||||
CTFontRef font = CTFontCreateWithGraphicsFont(cgFallback, fontSize, NULL, NULL);
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CFRelease(font);
|
||||
advance.width /= fontSize;
|
||||
advance.height /= fontSize;
|
||||
} else {
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
}
|
||||
CFRelease(cgFallback);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, NULL, &advance, IS_OSX_GT10_14);
|
||||
CFRelease(fallback);
|
||||
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
|
||||
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
|
||||
@@ -213,7 +201,7 @@ JNI_COCOA_ENTER(env);
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
|
||||
CGRect bbox;
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &tx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, &bbox, NULL, IS_OSX_GT10_14);
|
||||
CFRelease(fallback);
|
||||
|
||||
// the origin of this bounding box is relative to the bottom-left corner baseline
|
||||
@@ -230,20 +218,11 @@ JNI_COCOA_ENTER(env);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_CStrike
|
||||
* Method: getNativeGlyphOutline
|
||||
* Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_sun_font_CStrike_getNativeGlyphOutline
|
||||
(JNIEnv *env, jclass clazz,
|
||||
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos)
|
||||
jobject getGlyphOutline(JNIEnv *env, AWTStrike *awtStrike,
|
||||
CTFontRef font, CGGlyph glyph,
|
||||
jdouble xPos, jdouble yPos)
|
||||
{
|
||||
jobject generalPath = NULL;
|
||||
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
AWTPathRef path = NULL;
|
||||
jfloatArray pointCoords = NULL;
|
||||
jbyteArray pointTypes = NULL;
|
||||
@@ -253,21 +232,11 @@ JNI_COCOA_ENTER(env);
|
||||
|
||||
AWT_FONT_CLEANUP_SETUP;
|
||||
|
||||
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
||||
AWTFont *awtfont = awtStrike->fAWTFont;
|
||||
|
||||
AWT_FONT_CLEANUP_CHECK(awtfont);
|
||||
|
||||
// inverting the shear order and sign to compensate for the flipped coordinate system
|
||||
CGAffineTransform tx = awtStrike->fTx;
|
||||
tx.tx += xPos;
|
||||
tx.ty += yPos;
|
||||
|
||||
// get the right font and glyph for this "Java GlyphCode"
|
||||
|
||||
CGGlyph glyph;
|
||||
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
|
||||
|
||||
// get the advance of this glyph
|
||||
CGSize advance;
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
@@ -280,7 +249,6 @@ AWT_FONT_CLEANUP_CHECK(path);
|
||||
tx = awtStrike->fTx;
|
||||
tx = CGAffineTransformConcat(tx, sInverseTX);
|
||||
AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path);
|
||||
CFRelease(font);
|
||||
|
||||
pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements);
|
||||
AWT_FONT_CLEANUP_CHECK(pointCoords);
|
||||
@@ -314,10 +282,190 @@ cleanup:
|
||||
}
|
||||
|
||||
AWT_FONT_CLEANUP_FINISH;
|
||||
JNI_COCOA_EXIT(env);
|
||||
return generalPath;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_CStrike
|
||||
* Method: getNativeGlyphOutline
|
||||
* Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_sun_font_CStrike_getNativeGlyphOutline
|
||||
(JNIEnv *env, jclass clazz,
|
||||
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos) {
|
||||
jobject generalPath = NULL;
|
||||
|
||||
JNI_COCOA_ENTER(env);
|
||||
AWT_FONT_CLEANUP_SETUP;
|
||||
|
||||
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
||||
AWTFont *awtfont = awtStrike->fAWTFont;
|
||||
AWT_FONT_CLEANUP_CHECK(awtfont);
|
||||
|
||||
// get the right font and glyph for this "Java GlyphCode"
|
||||
CGGlyph glyph;
|
||||
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
|
||||
|
||||
generalPath = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);
|
||||
|
||||
cleanup:
|
||||
CFRelease(font);
|
||||
|
||||
AWT_FONT_CLEANUP_FINISH;
|
||||
JNI_COCOA_EXIT(env);
|
||||
|
||||
return generalPath;
|
||||
}
|
||||
|
||||
// OpenType data is Big-Endian
|
||||
#define GET_BE_INT32(data, i) CFSwapInt32BigToHost(*(const UInt32*) ((const UInt8*) (data) + (i)))
|
||||
#define GET_BE_INT16(data, i) CFSwapInt16BigToHost(*(const UInt16*) ((const UInt8*) (data) + (i)))
|
||||
|
||||
static bool addBitmapRenderData(JNIEnv *env, AWTStrike *awtStrike,
|
||||
CTFontRef font, CGGlyph glyph,
|
||||
jdouble xPos, jdouble yPos, jobject result) {
|
||||
bool success = false;
|
||||
|
||||
DECLARE_CLASS_RETURN(jc_GlyphRenderData, "sun/font/GlyphRenderData", false);
|
||||
DECLARE_METHOD_RETURN(GlyphRenderDataAddBitmap, jc_GlyphRenderData, "addBitmap", "(DDDDDDIIII[I)V", false);
|
||||
|
||||
AWT_FONT_CLEANUP_SETUP;
|
||||
|
||||
CTFontDescriptorRef descriptor = NULL;
|
||||
CGFontRef cgFont = CTFontCopyGraphicsFont(font, &descriptor);
|
||||
|
||||
CFDataRef sbixTable = CGFontCopyTableForTag(cgFont, kCTFontTableSbix);
|
||||
if (sbixTable == NULL) goto cleanup;
|
||||
|
||||
// Parse sbix table
|
||||
CFIndex sbixSize = CFDataGetLength(sbixTable);
|
||||
if (sbixSize < 8) goto cleanup; // Corrupted table
|
||||
const UInt8* sbix = CFDataGetBytePtr(sbixTable);
|
||||
UInt32 numStrikes = GET_BE_INT32(sbix, 4);
|
||||
if (8 + 4 * numStrikes > sbixSize) goto cleanup; // Corrupted table
|
||||
// Find last strike which has data for our glyph
|
||||
// Last is usually the biggest
|
||||
const UInt8* glyphData = NULL;
|
||||
UInt32 size;
|
||||
UInt16 ppem, ppi;
|
||||
for (int i = numStrikes - 1; i >= 0; i--) {
|
||||
const UInt8* strike = sbix + GET_BE_INT32(sbix, 8 + 4 * i);
|
||||
if (strike + 12 + 4 * glyph > sbix + sbixSize) goto cleanup; // Corrupted table
|
||||
UInt32 offset = GET_BE_INT32(strike, 4 + 4 * glyph);
|
||||
size = GET_BE_INT32(strike, 8 + 4 * glyph) - offset;
|
||||
if (size > 0) {
|
||||
ppem = GET_BE_INT16(strike, 0);
|
||||
ppi = GET_BE_INT16(strike, 2);
|
||||
glyphData = strike + offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (glyphData == NULL) goto cleanup;
|
||||
if (glyphData + 4 > sbix + sbixSize) goto cleanup; // Corrupted table
|
||||
|
||||
// Read glyph data
|
||||
FourCharCode graphicType = GET_BE_INT32(glyphData, 4);
|
||||
glyphData += 8;
|
||||
size -= 8;
|
||||
if (glyphData + size > sbix + sbixSize) goto cleanup; // Corrupted table
|
||||
|
||||
// Decode glyph image
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, glyphData, size, NULL);
|
||||
CGImageRef image = NULL;
|
||||
if (graphicType == 'jpg ') {
|
||||
image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
|
||||
} else if (graphicType == 'png ') {
|
||||
image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
|
||||
}
|
||||
CGDataProviderRelease(dataProvider);
|
||||
|
||||
if (image != NULL) {
|
||||
CGBitmapInfo info = CGImageGetBitmapInfo(image);
|
||||
size_t bits = CGImageGetBitsPerPixel(image);
|
||||
jint colorModel = -1;
|
||||
if (info & (kCGImageAlphaPremultipliedLast | kCGImageAlphaLast)) colorModel = 0; // RGBA
|
||||
else if (info & (kCGImageAlphaPremultipliedFirst | kCGImageAlphaFirst)) colorModel = 1; // ARGB
|
||||
if (colorModel != -1 && (info & kCGBitmapFloatComponents) == 0 && bits == 32) {
|
||||
size_t width = CGImageGetWidth(image);
|
||||
size_t height = CGImageGetHeight(image);
|
||||
size_t pitch = CGImageGetBytesPerRow(image) / 4;
|
||||
dataProvider = CGImageGetDataProvider(image);
|
||||
CFDataRef data = CGDataProviderCopyData(dataProvider);
|
||||
|
||||
jbyteArray array = (*env)->NewIntArray(env, pitch * height);
|
||||
(*env)->SetIntArrayRegion(env, array, 0, pitch * height, (const jint*) CFDataGetBytePtr(data));
|
||||
CFRelease(data);
|
||||
|
||||
double pointSize = 72.0 * ppem / ppi;
|
||||
double scale = 1.0 / pointSize;
|
||||
font = CTFontCreateWithGraphicsFont(cgFont, pointSize, NULL, descriptor);
|
||||
CGRect bbox = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, &glyph, NULL, 1);
|
||||
CFRelease(font);
|
||||
double tx = bbox.origin.x + xPos * pointSize / awtStrike->fSize;
|
||||
double ty = -bbox.origin.y - (double) height + yPos * pointSize / awtStrike->fSize;
|
||||
|
||||
jdouble m00 = awtStrike->fTx.a * scale, m10 = awtStrike->fTx.b * scale;
|
||||
jdouble m01 = -awtStrike->fTx.c * scale, m11 = -awtStrike->fTx.d * scale;
|
||||
jdouble m02 = m00 * tx + m01 * ty, m12 = m10 * tx + m11 * ty;
|
||||
|
||||
(*env)->CallVoidMethod(env, result, GlyphRenderDataAddBitmap,
|
||||
m00, m10, m01, m11, m02, m12,
|
||||
width, height, pitch, 0, array);
|
||||
success = true;
|
||||
}
|
||||
CGImageRelease(image);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cleanup:
|
||||
if (sbixTable) CFRelease(sbixTable);
|
||||
if (cgFont) CFRelease(cgFont);
|
||||
if (descriptor) CFRelease(descriptor);
|
||||
|
||||
AWT_FONT_CLEANUP_FINISH;
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_CStrike
|
||||
* Method: getNativeGlyphRenderData
|
||||
* Signature: (JIDDLsun/font/GlyphRenderData;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_font_CStrike_getNativeGlyphRenderData
|
||||
(JNIEnv *env, jclass clazz,
|
||||
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos, jobject result)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
DECLARE_CLASS(jc_GlyphRenderData, "sun/font/GlyphRenderData");
|
||||
DECLARE_FIELD(GlyphRenderDataOutline, jc_GlyphRenderData, "outline", "Ljava/awt/geom/GeneralPath;")
|
||||
|
||||
AWT_FONT_CLEANUP_SETUP;
|
||||
|
||||
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
||||
AWTFont *awtfont = awtStrike->fAWTFont;
|
||||
AWT_FONT_CLEANUP_CHECK(awtfont);
|
||||
|
||||
// get the right font and glyph for this "Java GlyphCode"
|
||||
CGGlyph glyph;
|
||||
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
|
||||
|
||||
if (!addBitmapRenderData(env, awtStrike, font, glyph, xPos, yPos, result)) {
|
||||
jobject gp = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);
|
||||
if (gp != NULL) {
|
||||
(*env)->SetObjectField(env, result, GlyphRenderDataOutline, gp);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
CFRelease(font);
|
||||
|
||||
AWT_FONT_CLEANUP_FINISH;
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_CStrike
|
||||
* Method: getNativeGlyphOutlineBounds
|
||||
|
||||
@@ -118,16 +118,17 @@ JNI_COCOA_EXIT(env);
|
||||
/*
|
||||
* Class: sun_font_CCompositeGlyphMapper
|
||||
* Method: nativeCodePointToGlyph
|
||||
* Signature: (JI[Ljava/lang/String;)I
|
||||
* Signature: (JII[Ljava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_font_CCompositeGlyphMapper_nativeCodePointToGlyph
|
||||
(JNIEnv *env, jclass clazz, jlong awtFontPtr, jint codePoint, jobjectArray resultArray)
|
||||
(JNIEnv *env, jclass clazz, jlong awtFontPtr, jint codePoint, jint variationSelector, jobjectArray resultArray)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
|
||||
CFStringRef fontNames[] = {NULL, NULL};
|
||||
CGGlyph glyph = CTS_CopyGlyphAndFontNamesForCodePoint(awtFont, (UnicodeScalarValue)codePoint, fontNames);
|
||||
CGGlyph glyph = CTS_CopyGlyphAndFontNamesForCodePoint(awtFont, (UnicodeScalarValue)codePoint,
|
||||
(UnicodeScalarValue)variationSelector, fontNames);
|
||||
if (glyph > 0) {
|
||||
jstring fontName = (jstring)NSStringToJavaString(env, (NSString *)fontNames[0]);
|
||||
(*env)->SetObjectArrayElement(env, resultArray, 0, fontName);
|
||||
|
||||
@@ -34,6 +34,17 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
|
||||
const AWTStrike *strike,
|
||||
jint rawGlyphCodes[], const CFIndex len);
|
||||
|
||||
void
|
||||
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
|
||||
const CGAffineTransform *tx,
|
||||
CGFloat fontSize,
|
||||
const JRSFontRenderingStyle style,
|
||||
const CGGlyph glyphs[],
|
||||
size_t count,
|
||||
CGRect bboxes[],
|
||||
CGSize advances[],
|
||||
const bool isCatalinaOrAbove);
|
||||
|
||||
bool CGGI_IsColorFont(CGFontRef font);
|
||||
|
||||
#endif /* __CGGLYPHIMAGES_H */
|
||||
|
||||
@@ -664,6 +664,16 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
|
||||
#define RENDER_GLYPH_BATCH_SIZE 16
|
||||
#define RENDER_GLYPH_ARRAY_INIT_8 glyph,glyph,glyph,glyph,glyph,glyph,glyph,glyph
|
||||
#define RENDER_GLYPH_ARRAY_INIT RENDER_GLYPH_ARRAY_INIT_8,RENDER_GLYPH_ARRAY_INIT_8
|
||||
|
||||
static CTFontRef CopyFontWithSize(CTFontRef originalFont, CGFloat size) {
|
||||
CTFontDescriptorRef descriptor = NULL;
|
||||
CGFontRef cgFont = CTFontCopyGraphicsFont(originalFont, &descriptor);
|
||||
CTFontRef result = CTFontCreateWithGraphicsFont(cgFont, size, NULL, descriptor);
|
||||
if (cgFont) CFRelease(cgFont);
|
||||
if (descriptor) CFRelease(descriptor);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the canvas, strikes the glyph with CoreGraphics, and then
|
||||
* copies the struck pixels into the GlyphInfo image.
|
||||
@@ -696,7 +706,8 @@ CGGI_CreateImageForGlyph
|
||||
CGPoint subpixelOffset = CGPointMake(1.0 / (float) info->subpixelResolutionX, 1.0 / (float) info->subpixelResolutionY);
|
||||
if (isCatalinaOrAbove || glyphDescriptor == &argb) {
|
||||
CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);
|
||||
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
|
||||
CGFloat fontSize = glyphDescriptor != &argb ? strike->fSize :
|
||||
sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
|
||||
CTFontRef font = CTFontCreateWithGraphicsFont(cgFont, fontSize, NULL, NULL);
|
||||
|
||||
CGFloat normFactor = 1.0 / fontSize;
|
||||
@@ -728,16 +739,6 @@ CGGI_CreateImageForGlyph
|
||||
CTFontDrawGlyphs(font, glyphs, glyphPositions, glyphIndex, canvas->context);
|
||||
}
|
||||
|
||||
// CTFontGetAdvancesForGlyphs returns rounded advance for emoji font, so it can be calculated only here
|
||||
// where CTFont instance with actual size is available
|
||||
CGSize advance;
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
advance.width /= fontSize;
|
||||
advance.height /= fontSize;
|
||||
advance = CGGI_ScaleAdvance(advance, strike);
|
||||
info->advanceX = advance.width;
|
||||
info->advanceY = advance.height;
|
||||
|
||||
CFRelease(font);
|
||||
// restore context's original state
|
||||
CGContextSetTextMatrix(canvas->context, matrix);
|
||||
@@ -793,10 +794,8 @@ CGGI_CreateImageForUnicode
|
||||
bool subpixelResolution = mode->subpixelResolution && glyphDescriptor == &grey;
|
||||
|
||||
CGRect bbox;
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &strike->fTx, style, &glyph, 1, &bbox);
|
||||
|
||||
CGSize advance;
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &strike->fTx, strike->fSize, style, &glyph, 1, &bbox, &advance, isCatalinaOrAbove);
|
||||
|
||||
|
||||
// create the Sun2D GlyphInfo we are going to strike into
|
||||
@@ -954,8 +953,8 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
|
||||
AWTFont *font = strike->fAWTFont;
|
||||
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
|
||||
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &strike->fTx, bboxCGMode, glyphs, len, bboxes);
|
||||
CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
|
||||
CTFontRef fontRef = (CTFontRef)font->fFont;
|
||||
CGGlyphImages_GetGlyphMetrics(fontRef, &strike->fTx, strike->fSize, bboxCGMode, glyphs, len, bboxes, advances, IS_OSX_GT10_14);
|
||||
|
||||
size_t maxWidth = 1;
|
||||
size_t maxHeight = 1;
|
||||
@@ -1073,3 +1072,60 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs.
|
||||
*/
|
||||
void
|
||||
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
|
||||
const CGAffineTransform *tx,
|
||||
CGFloat fontSize,
|
||||
const JRSFontRenderingStyle style,
|
||||
const CGGlyph glyphs[],
|
||||
size_t count,
|
||||
CGRect bboxes[],
|
||||
CGSize advances[],
|
||||
const bool isCatalinaOrAbove) {
|
||||
if (isCatalinaOrAbove || CGGI_IsColorFont(font)) {
|
||||
// Glyph metrics for emoji font are not strictly proportional to font size,
|
||||
// so we need to construct real-sized font object to calculate them.
|
||||
// The logic here must match the logic in CGGI_CreateImageForGlyph,
|
||||
// which performs glyph drawing.
|
||||
|
||||
CTFontRef sizedFont = CopyFontWithSize(font, fontSize);
|
||||
|
||||
if (bboxes) {
|
||||
// JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font:
|
||||
// it uses bottom left corner of the glyph's bounding box as a fixed point of transformation
|
||||
// instead of glyph's origin point (used at drawing). So, as a workaround,
|
||||
// we request a bounding box for the untransformed glyph, and apply the transform ourselves.
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(sizedFont, &CGAffineTransformIdentity, style, glyphs, count, bboxes);
|
||||
CGAffineTransform txNormalized = CGAffineTransformMake(tx->a / fontSize,
|
||||
tx->b / fontSize,
|
||||
tx->c / fontSize,
|
||||
tx->d / fontSize,
|
||||
0, 0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
bboxes[i] = CGRectApplyAffineTransform(bboxes[i], txNormalized);
|
||||
}
|
||||
}
|
||||
|
||||
if (advances) {
|
||||
CTFontGetAdvancesForGlyphs(sizedFont, kCTFontDefaultOrientation, glyphs, advances, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Calling code will scale the result back
|
||||
advances[i].width /= fontSize;
|
||||
advances[i].height /= fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(sizedFont);
|
||||
} else {
|
||||
if (bboxes) {
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(font, tx, style, glyphs, count, bboxes);
|
||||
}
|
||||
if (advances) {
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
#define HI_SURROGATE_END 0xDBFF
|
||||
#define LO_SURROGATE_START 0xDC00
|
||||
#define LO_SURROGATE_END 0xDFFF
|
||||
#define VS_START 0xFE00
|
||||
#define VS_END 0xFE0F
|
||||
#define VSS_START 0xE0100
|
||||
#define VSS_END 0xE01FF
|
||||
|
||||
/*
|
||||
* Transform Unicode characters into glyphs.
|
||||
@@ -57,11 +61,14 @@ CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode(const AWTFont *font, const UT
|
||||
// Transform a single Unicode character code into glyph code.
|
||||
// Names of the relevant font are also returned, if the substitution is used.
|
||||
// Non-null components of fontNames array should always be released by the calling code, regardless of the returned value.
|
||||
CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint(const AWTFont *font, const UnicodeScalarValue codePoint, CFStringRef fontNames[]);
|
||||
CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint(const AWTFont *font, const UnicodeScalarValue codePoint, const UnicodeScalarValue variationSelector, CFStringRef fontNames[]);
|
||||
|
||||
// Breakup a 32 bit unicode value into the component surrogate pairs
|
||||
void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]);
|
||||
|
||||
// Get number of UTF16 values from 32 bit unicode value (2 for surrogate pair and 1 otherwise)
|
||||
int CTS_GetUnicodeSize(const UnicodeScalarValue unicode);
|
||||
|
||||
|
||||
// Basic struct that holds everything CoreText is interested in
|
||||
typedef struct CTS_ProviderStruct {
|
||||
|
||||
@@ -88,29 +88,44 @@ ReleaseCTStateDictionary(CFDictionaryRef ctStateDict)
|
||||
CFRelease(ctStateDict); // GC
|
||||
}
|
||||
|
||||
int NextUnicode(const UniChar unicodes[], UnicodeScalarValue *codePoint, const size_t index, const size_t limit) {
|
||||
if (index >= limit) return 0;
|
||||
UniChar unicode = unicodes[index];
|
||||
UniChar nextUnicode = (index+1) < limit ? unicodes[index+1] : 0;
|
||||
bool surrogatePair = unicode >= HI_SURROGATE_START && unicode <= HI_SURROGATE_END
|
||||
&& nextUnicode >= LO_SURROGATE_START && nextUnicode <= LO_SURROGATE_END;
|
||||
*codePoint = surrogatePair ? (((int)(unicode - HI_SURROGATE_START)) << 10)
|
||||
+ nextUnicode - LO_SURROGATE_START + 0x10000 : unicode;
|
||||
return surrogatePair ? 2 : 1;
|
||||
}
|
||||
|
||||
void GetFontsAndGlyphsForCharacters(CTFontRef font, CTFontRef fallbackBase,
|
||||
const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[],
|
||||
CTFontRef actualFonts[], const size_t count)
|
||||
{
|
||||
CTFontGetGlyphsForCharacters(font, unicodes, glyphs, count);
|
||||
if (!fallbackBase) fallbackBase = font;
|
||||
size_t i;
|
||||
for (i = 0; i < count; i++) {
|
||||
UniChar unicode = unicodes[i];
|
||||
UniChar nextUnicode = (i+1) < count ? unicodes[i+1] : 0;
|
||||
bool surrogatePair = unicode >= HI_SURROGATE_START && unicode <= HI_SURROGATE_END
|
||||
&& nextUnicode >= LO_SURROGATE_START && nextUnicode <= LO_SURROGATE_END;
|
||||
size_t i, size;
|
||||
for (i = 0; i < count; i += size) {
|
||||
UnicodeScalarValue codePoint, variationCodePoint;
|
||||
int codePointSize = size = NextUnicode(unicodes, &codePoint, i, count);
|
||||
if (size == 0) break;
|
||||
|
||||
int variationSize = NextUnicode(unicodes, &variationCodePoint, i + size , count);
|
||||
bool hasVariationSelector = variationSize > 0 &&
|
||||
((variationCodePoint >= VSS_START && variationCodePoint <= VSS_END) ||
|
||||
(variationCodePoint >= VS_START && variationCodePoint <= VS_END));
|
||||
if (hasVariationSelector) size += variationSize;
|
||||
|
||||
CGGlyph glyph = glyphs[i];
|
||||
if (glyph > 0) {
|
||||
if (glyph > 0 && (!hasVariationSelector || glyphs[i + codePointSize] > 0)) {
|
||||
glyphsAsInts[i] = glyph;
|
||||
if (surrogatePair) i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, &unicodes[i], surrogatePair ? 2 : 1);
|
||||
const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, &unicodes[i], size);
|
||||
if (fallback) {
|
||||
CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], surrogatePair ? 2 : 1);
|
||||
CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], size);
|
||||
glyph = glyphs[i];
|
||||
if (actualFonts && glyph > 0) {
|
||||
actualFonts[i] = fallback;
|
||||
@@ -120,13 +135,10 @@ void GetFontsAndGlyphsForCharacters(CTFontRef font, CTFontRef fallbackBase,
|
||||
}
|
||||
|
||||
if (glyph > 0) {
|
||||
int codePoint = surrogatePair ? (((int)(unicode - HI_SURROGATE_START)) << 10)
|
||||
+ nextUnicode - LO_SURROGATE_START + 0x10000 : unicode;
|
||||
glyphsAsInts[i] = -codePoint; // set the glyph code to the negative unicode value
|
||||
} else {
|
||||
glyphsAsInts[i] = 0; // CoreText couldn't find a glyph for this character either
|
||||
}
|
||||
if (surrogatePair) i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,17 +162,26 @@ void CTS_GetGlyphsAsIntsForCharacters
|
||||
* Names of the corresponding substituted font are also returned if substitution is performed.
|
||||
*/
|
||||
CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint
|
||||
(const AWTFont *font, const UnicodeScalarValue codePoint, CFStringRef fontNames[])
|
||||
(const AWTFont *font, const UnicodeScalarValue codePoint, const UnicodeScalarValue variationSelector, CFStringRef fontNames[])
|
||||
{
|
||||
CTFontRef fontRef = (CTFontRef)font->fFont;
|
||||
CTFontRef fallbackBase = (CTFontRef)font->fFallbackBase;
|
||||
int count = codePoint >= 0x10000 ? 2 : 1;
|
||||
int codePointSize = CTS_GetUnicodeSize(codePoint);
|
||||
int count = codePointSize + (variationSelector == 0 ? 0 : CTS_GetUnicodeSize(variationSelector));
|
||||
UTF16Char unicodes[count];
|
||||
if (count == 1) {
|
||||
if (codePoint < 0x10000) {
|
||||
unicodes[0] = (UTF16Char)codePoint;
|
||||
} else {
|
||||
CTS_BreakupUnicodeIntoSurrogatePairs(codePoint, unicodes);
|
||||
}
|
||||
if (variationSelector != 0) {
|
||||
UTF16Char* codes = &unicodes[codePointSize];
|
||||
if (variationSelector < 0x10000) {
|
||||
codes[0] = (UTF16Char)variationSelector;
|
||||
} else {
|
||||
CTS_BreakupUnicodeIntoSurrogatePairs(variationSelector, codes);
|
||||
}
|
||||
}
|
||||
CGGlyph glyphs[count];
|
||||
jint glyphsAsInts[count];
|
||||
CTFontRef actualFonts[count];
|
||||
@@ -246,3 +267,7 @@ void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
|
||||
charRef[0] = high_surrogate;
|
||||
charRef[1] = low_surrogate;
|
||||
}
|
||||
|
||||
int CTS_GetUnicodeSize(const UnicodeScalarValue unicode) {
|
||||
return unicode >= 0x10000 ? 2 : 1;
|
||||
}
|
||||
|
||||
@@ -380,6 +380,11 @@ final class SMFParser {
|
||||
case 0xF7:
|
||||
// sys ex
|
||||
int sysexLength = (int) readVarInt();
|
||||
if (sysexLength < 0 || sysexLength > trackLength - pos) {
|
||||
throw new InvalidMidiDataException("Message length is out of bounds: "
|
||||
+ sysexLength);
|
||||
}
|
||||
|
||||
byte[] sysexData = new byte[sysexLength];
|
||||
read(sysexData);
|
||||
|
||||
@@ -392,8 +397,8 @@ final class SMFParser {
|
||||
// meta
|
||||
int metaType = readUnsigned();
|
||||
int metaLength = (int) readVarInt();
|
||||
if (metaLength < 0) {
|
||||
throw new InvalidMidiDataException("length out of bounds: "
|
||||
if (metaLength < 0 || metaLength > trackLength - pos) {
|
||||
throw new InvalidMidiDataException("Message length is out of bounds: "
|
||||
+ metaLength);
|
||||
}
|
||||
final byte[] metaData;
|
||||
|
||||
@@ -444,7 +444,11 @@ public class AWTThreading {
|
||||
synchronized (eventDispatchThreadStateNotifiers) {
|
||||
if (!isEventDispatchThreadFree) {
|
||||
eventDispatchThreadStateNotifiers.add(future);
|
||||
future.whenComplete((r, ex) -> eventDispatchThreadStateNotifiers.remove(future));
|
||||
future.whenComplete((r, ex) -> {
|
||||
synchronized (eventDispatchThreadStateNotifiers) {
|
||||
eventDispatchThreadStateNotifiers.remove(future);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,7 +1087,7 @@ abstract class CMap {
|
||||
|
||||
public static final NullCMapClass theNullCmap = new NullCMapClass();
|
||||
|
||||
final int getControlCodeGlyph(int charCode, boolean noSurrogates) {
|
||||
static int getControlCodeGlyph(int charCode, boolean noSurrogates) {
|
||||
if (charCode < 0x0010) {
|
||||
switch (charCode) {
|
||||
case 0x0009:
|
||||
@@ -1100,7 +1100,7 @@ abstract class CMap {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final char getFormatCharGlyph(int charCode) {
|
||||
static char getFormatCharGlyph(int charCode) {
|
||||
if (charCode >= 0x200c) {
|
||||
if ((charCode <= 0x200f) ||
|
||||
(charCode >= 0x2028 && charCode <= 0x202e) ||
|
||||
@@ -1167,15 +1167,17 @@ abstract class CMap {
|
||||
}
|
||||
}
|
||||
|
||||
static final int VS_NOGLYPH = 0;
|
||||
private int getGlyph(int charCode, int variationSelector) {
|
||||
int targetSelector = -1;
|
||||
private int findVariationSelectorIndex(int variationSelector) {
|
||||
for (int i = 0; i < numSelectors; i++) {
|
||||
if (selector[i] == variationSelector) {
|
||||
targetSelector = i;
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static final int VS_NOGLYPH = 0;
|
||||
private int getGlyph(int charCode, int targetSelector) {
|
||||
if (targetSelector == -1) {
|
||||
return VS_NOGLYPH;
|
||||
}
|
||||
@@ -1191,15 +1193,17 @@ abstract class CMap {
|
||||
}
|
||||
|
||||
char getVariationGlyph(int charCode, int variationSelector) {
|
||||
if (variationSelector == 0) return getGlyph(charCode);
|
||||
char glyph = 0;
|
||||
if (uvs == null) {
|
||||
glyph = getGlyph(charCode);
|
||||
} else {
|
||||
int result = uvs.getGlyph(charCode, variationSelector);
|
||||
if (result > 0) {
|
||||
glyph = (char)(result & 0xFFFF);
|
||||
} else {
|
||||
glyph = getGlyph(charCode);
|
||||
if (uvs != null) {
|
||||
int targetSelector = uvs.findVariationSelectorIndex(variationSelector);
|
||||
if (targetSelector != -1) {
|
||||
int result = uvs.getGlyph(charCode, targetSelector);
|
||||
if (result > 0) {
|
||||
glyph = (char)(result & 0xFFFF);
|
||||
} else {
|
||||
glyph = getGlyph(charCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return glyph;
|
||||
|
||||
@@ -109,14 +109,18 @@ public class CompositeGlyphMapper extends CharToGlyphMapper {
|
||||
}
|
||||
|
||||
protected int convertToGlyph(int unicode) {
|
||||
return convertToGlyph(unicode, 0);
|
||||
}
|
||||
|
||||
protected int convertToGlyph(int unicode, int variationSelector) {
|
||||
|
||||
for (int slot = 0; slot < font.numSlots; slot++) {
|
||||
if (!hasExcludes || !font.isExcludedChar(slot, unicode)) {
|
||||
CharToGlyphMapper mapper = getSlotMapper(slot);
|
||||
int glyphCode = mapper.charToGlyph(unicode);
|
||||
int glyphCode = mapper.charToVariationGlyph(unicode, variationSelector);
|
||||
if (glyphCode != mapper.getMissingGlyphCode()) {
|
||||
glyphCode = compositeGlyphCode(slot, glyphCode);
|
||||
setCachedGlyphCode(unicode, glyphCode);
|
||||
if (variationSelector == 0) setCachedGlyphCode(unicode, glyphCode);
|
||||
return glyphCode;
|
||||
}
|
||||
}
|
||||
@@ -124,6 +128,28 @@ public class CompositeGlyphMapper extends CharToGlyphMapper {
|
||||
return missingGlyph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int charToVariationGlyph(int unicode, int variationSelector) {
|
||||
if (variationSelector == 0) return charToGlyph(unicode);
|
||||
else {
|
||||
int glyph = convertToGlyph(unicode, variationSelector);
|
||||
// Glyph variation not found, fallback to base glyph.
|
||||
// In fallback from variation glyph we ignore excluded chars,
|
||||
// this is needed for proper display of monochrome emoji (\ufe0e)
|
||||
if (glyph == missingGlyph) {
|
||||
for (int slot = 0; slot < font.numSlots; slot++) {
|
||||
CharToGlyphMapper mapper = getSlotMapper(slot);
|
||||
glyph = mapper.charToGlyph(unicode);
|
||||
if (glyph != mapper.getMissingGlyphCode()) {
|
||||
glyph = compositeGlyphCode(slot, glyph);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return glyph;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
int numGlyphs = 0;
|
||||
/* The number of glyphs in a composite is affected by
|
||||
|
||||
@@ -214,4 +214,9 @@ public final class CompositeStrike extends FontStrike {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
return strike.getGlyphRenderData(glyphCode & SLOTMASK, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,16 @@ public abstract class FileFont extends PhysicalFont {
|
||||
}
|
||||
}
|
||||
|
||||
public GlyphRenderData getGlyphRenderData(long pScalerContext, int glyphCode,
|
||||
float x, float y) {
|
||||
try {
|
||||
return getScaler().getGlyphRenderData(pScalerContext, glyphCode, x, y);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphRenderData(pScalerContext, glyphCode, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/* T1 & TT implementation differ so this method is abstract.
|
||||
NB: null should not be returned here! */
|
||||
protected abstract FontScaler getScaler();
|
||||
|
||||
@@ -1076,6 +1076,36 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
glyphs, glyphs.length, x, y);
|
||||
}
|
||||
|
||||
private
|
||||
WeakReference<ConcurrentHashMap<Integer, GlyphRenderData>> glyphRenderDataMapRef;
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
|
||||
GlyphRenderData grd = null;
|
||||
ConcurrentHashMap<Integer, GlyphRenderData> glyphRenderDataMap = null;
|
||||
|
||||
if (glyphRenderDataMapRef != null) {
|
||||
glyphRenderDataMap = glyphRenderDataMapRef.get();
|
||||
if (glyphRenderDataMap != null) {
|
||||
grd = glyphRenderDataMap.get(glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (grd == null) {
|
||||
grd = fileFont.getGlyphRenderData(pScalerContext, glyphCode, 0, 0);
|
||||
if (glyphRenderDataMap == null) {
|
||||
glyphRenderDataMap = new ConcurrentHashMap<>();
|
||||
glyphRenderDataMapRef = new WeakReference<>(glyphRenderDataMap);
|
||||
}
|
||||
glyphRenderDataMap.put(glyphCode, grd);
|
||||
}
|
||||
grd = new GlyphRenderData(grd); // mutable!
|
||||
if (x != 0f || y != 0f) {
|
||||
grd.transform(AffineTransform.getTranslateInstance(x, y));
|
||||
}
|
||||
return grd;
|
||||
}
|
||||
|
||||
protected void adjustPoint(Point2D.Float pt) {
|
||||
if (invertDevTx != null) {
|
||||
invertDevTx.deltaTransform(pt, pt);
|
||||
|
||||
@@ -118,14 +118,34 @@ public final class FontRunIterator {
|
||||
}
|
||||
|
||||
int ch = nextCodePoint(lim);
|
||||
int sl = mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK;
|
||||
int nch = nextCodePoint(lim);
|
||||
int vs = CharToGlyphMapper.isVariationSelector(nch) ? nch : 0;
|
||||
int sl = mapper.charToVariationGlyph(ch, vs) & CompositeGlyphMapper.SLOTMASK;
|
||||
slot = sl >>> 24;
|
||||
while ((ch = nextCodePoint(lim)) != DONE && (mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK) == sl);
|
||||
do {
|
||||
if (vs == 0) {
|
||||
ch = nch;
|
||||
} else {
|
||||
ch = nextCodePoint(lim);
|
||||
}
|
||||
nch = nextCodePoint(lim);
|
||||
vs = CharToGlyphMapper.isVariationSelector(nch) ? nch : 0;
|
||||
} while(ch != DONE && isSameRun(ch, vs, sl));
|
||||
pushback(nch);
|
||||
pushback(ch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSameRun(int ch, int variationSelector, int currentSlot) {
|
||||
// Every font is meant to be able to render format chars
|
||||
// So we make format chars stick to the current font run
|
||||
if (CMap.getFormatCharGlyph(ch) == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
|
||||
return true;
|
||||
}
|
||||
return (mapper.charToVariationGlyph(ch, variationSelector) & CompositeGlyphMapper.SLOTMASK) == currentSlot;
|
||||
}
|
||||
|
||||
public boolean next() {
|
||||
return next(Script.COMMON, limit);
|
||||
}
|
||||
|
||||
@@ -168,6 +168,10 @@ public abstract class FontScaler implements DisposerRecord {
|
||||
int numGlyphs, float x, float y)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract GlyphRenderData getGlyphRenderData(long pScalerContext, int glyphCode,
|
||||
float x, float y)
|
||||
throws FontScalerException;
|
||||
|
||||
/* Used by Java2D disposer to ensure native resources are released.
|
||||
Note: this method does not release any of created
|
||||
scaler context objects! */
|
||||
|
||||
@@ -76,5 +76,6 @@ public abstract class FontStrike {
|
||||
abstract GeneralPath
|
||||
getGlyphVectorOutline(int[] glyphs, float x, float y);
|
||||
|
||||
abstract GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y);
|
||||
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ public final class FontUtilities {
|
||||
* where the caller interprets 'layout' to mean any case where
|
||||
* one 'char' (ie the java type char) does not map to one glyph
|
||||
*/
|
||||
public static final int MAX_LAYOUT_CHARCODE = 0x20F0;
|
||||
public static final int MAX_LAYOUT_CHARCODE = CharToGlyphMapper.VSS_END;
|
||||
|
||||
/**
|
||||
* Calls the private getFont2D() method in java.awt.Font objects.
|
||||
@@ -340,7 +340,19 @@ public final class FontUtilities {
|
||||
else if (code >= 0x206a && code <= 0x206f) { // directional control
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x20d0) { // U+20D0 - U+20D0 combining diacritical marks for symbols
|
||||
else if (code >= 0x20d0 && code <= 0x20f0) { // U+20D0 - U+20F0 combining diacritical marks for symbols
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x1f1e6 && code <= 0x1f1ff) { // U+1F1E6 - U+1F1FF flag letters https://emojipedia.org/emoji-flag-sequence/
|
||||
return true;
|
||||
}
|
||||
else if (code == 0x1f3f4) { // black flag https://emojipedia.org/emoji-tag-sequence/
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x1f3fb && code <= 0x1f3ff) { // U+1F3FB - U+1F3FF emoji modifiers
|
||||
return true;
|
||||
}
|
||||
else if (CharToGlyphMapper.isVariationSelector(code)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -194,6 +194,21 @@ class FreetypeFontScaler extends FontScaler {
|
||||
.getNullScaler().getGlyphVectorOutline(0L, glyphs, numGlyphs, x, y);
|
||||
}
|
||||
|
||||
synchronized GlyphRenderData getGlyphRenderData(long pScalerContext, int glyphCode,
|
||||
float x, float y) throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
GlyphRenderData result = new GlyphRenderData();
|
||||
getGlyphRenderDataNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode,
|
||||
x, y, result);
|
||||
return result;
|
||||
}
|
||||
return FontScaler.getNullScaler().
|
||||
getGlyphRenderData(0L, glyphCode, x,y);
|
||||
}
|
||||
|
||||
/* This method should not be called directly, in case
|
||||
* it is being invoked from a thread with a native context.
|
||||
*/
|
||||
@@ -285,6 +300,9 @@ class FreetypeFontScaler extends FontScaler {
|
||||
private native GeneralPath getGlyphVectorOutlineNative(Font2D font,
|
||||
long pScalerContext, long pScaler,
|
||||
int[] glyphs, int numGlyphs, float x, float y);
|
||||
private native void getGlyphRenderDataNative(Font2D font, long pScalerContext,
|
||||
long pScaler, int glyphCode,
|
||||
float x, float y, GlyphRenderData result);
|
||||
private native Point2D.Float getGlyphPointNative(Font2D font,
|
||||
long pScalerContext, long pScaler, int glyphCode, int ptNumber);
|
||||
|
||||
|
||||
184
src/java.desktop/share/classes/sun/font/GlyphRenderData.java
Normal file
184
src/java.desktop/share/classes/sun/font/GlyphRenderData.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2000-2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.image.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data for rendering any number of glyphs bypassing glyph cache.
|
||||
*/
|
||||
public class GlyphRenderData {
|
||||
|
||||
public GeneralPath outline;
|
||||
public List<ColorLayer> colorLayers;
|
||||
public List<Bitmap> bitmaps;
|
||||
|
||||
public GlyphRenderData() {}
|
||||
public GlyphRenderData(GlyphRenderData i) {
|
||||
if (i.outline != null) outline = (GeneralPath) i.outline.clone();
|
||||
if (i.colorLayers != null) {
|
||||
colorLayers = new ArrayList<>(i.colorLayers.size());
|
||||
for (ColorLayer l : i.colorLayers) {
|
||||
colorLayers.add(new ColorLayer(l.color, (GeneralPath) l.outline.clone()));
|
||||
}
|
||||
}
|
||||
if (i.bitmaps != null) {
|
||||
bitmaps = new ArrayList<>(i.bitmaps.size());
|
||||
for (Bitmap b : i.bitmaps) {
|
||||
bitmaps.add(new Bitmap(new AffineTransform(b.transform), b.image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i must not be used afterwards
|
||||
*/
|
||||
public void merge(GlyphRenderData i) {
|
||||
if (i.outline != null) {
|
||||
if (outline == null) outline = i.outline;
|
||||
else outline.append(i.outline.getPathIterator(null), false);
|
||||
}
|
||||
if (i.colorLayers != null) {
|
||||
if (colorLayers == null) colorLayers = i.colorLayers;
|
||||
else colorLayers.addAll(i.colorLayers);
|
||||
}
|
||||
if (i.bitmaps != null) {
|
||||
if (bitmaps == null) bitmaps = i.bitmaps;
|
||||
else bitmaps.addAll(i.bitmaps);
|
||||
}
|
||||
}
|
||||
|
||||
public void transform(AffineTransform transform) {
|
||||
if (outline != null) outline.transform(transform);
|
||||
if (colorLayers != null) {
|
||||
for (ColorLayer layer : colorLayers) {
|
||||
layer.outline.transform(transform);
|
||||
}
|
||||
}
|
||||
if (bitmaps != null) {
|
||||
for (Bitmap bitmap : bitmaps) {
|
||||
bitmap.transform.preConcatenate(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(Graphics2D g) {
|
||||
if (outline != null) g.fill(outline);
|
||||
if (colorLayers != null) {
|
||||
Color color = g.getColor();
|
||||
for (ColorLayer layer : colorLayers) {
|
||||
g.setColor(layer.color == null ? color : layer.color);
|
||||
g.fill(layer.outline);
|
||||
}
|
||||
g.setColor(color);
|
||||
}
|
||||
if (bitmaps != null) {
|
||||
for (Bitmap bitmap : bitmaps) {
|
||||
g.drawImage(bitmap.image, bitmap.transform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColorLayer {
|
||||
public final Color color;
|
||||
public final GeneralPath outline;
|
||||
|
||||
public ColorLayer(Color color, GeneralPath outline) {
|
||||
this.color = color;
|
||||
this.outline = outline;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bitmap {
|
||||
public final AffineTransform transform;
|
||||
public final Image image;
|
||||
|
||||
public Bitmap(AffineTransform transform, Image image) {
|
||||
this.transform = transform;
|
||||
this.image = image;
|
||||
}
|
||||
}
|
||||
|
||||
// These methods exist for convenience and are called from native
|
||||
|
||||
private void setColorLayersList(int capacity) {
|
||||
colorLayers = new ArrayList<>(capacity);
|
||||
}
|
||||
|
||||
private void addColorLayers(GeneralPath outline) {
|
||||
colorLayers.add(new ColorLayer(null, outline));
|
||||
}
|
||||
|
||||
private void addColorLayers(int r, int g, int b, int a, GeneralPath outline) {
|
||||
colorLayers.add(new ColorLayer(new Color(r, g, b, a), outline));
|
||||
}
|
||||
|
||||
private static DirectColorModel colorModel(boolean premultiplied, int bits, int r, int g, int b, int a) {
|
||||
if (Unsafe.getUnsafe().isBigEndian()) {
|
||||
r = Integer.reverse(r) >>> (32 - bits);
|
||||
g = Integer.reverse(g) >>> (32 - bits);
|
||||
b = Integer.reverse(b) >>> (32 - bits);
|
||||
a = Integer.reverse(a) >>> (32 - bits);
|
||||
}
|
||||
return new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
bits, r, g, b, a, premultiplied, DataBuffer.TYPE_INT);
|
||||
}
|
||||
private static final DirectColorModel[] BITMAP_COLOR_MODELS = {
|
||||
colorModel(false, 32, // macOS RGBA
|
||||
0x000000ff,
|
||||
0x0000ff00,
|
||||
0x00ff0000,
|
||||
0xff000000),
|
||||
colorModel(false, 32, // macOS ARGB
|
||||
0x0000ff00,
|
||||
0x00ff0000,
|
||||
0xff000000,
|
||||
0x000000ff),
|
||||
colorModel(true, 32, // Freetype BGRA
|
||||
0x00ff0000,
|
||||
0x0000ff00,
|
||||
0x000000ff,
|
||||
0xff000000)
|
||||
};
|
||||
private void addBitmap(double m00, double m10,
|
||||
double m01, double m11,
|
||||
double m02, double m12,
|
||||
int width, int height, int pitch,
|
||||
int colorModel, int[] data) {
|
||||
if (bitmaps == null) bitmaps = new ArrayList<>();
|
||||
DirectColorModel color = BITMAP_COLOR_MODELS[colorModel];
|
||||
DataBuffer buffer = new DataBufferInt(data, data.length);
|
||||
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, pitch, color.getMasks(), null);
|
||||
BufferedImage image = new BufferedImage(color, raster, color.isAlphaPremultiplied(), null);
|
||||
bitmaps.add(new Bitmap(new AffineTransform(m00, m10, m01, m11, m02, m12), image));
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,11 @@ class NullFontScaler extends FontScaler {
|
||||
return new GeneralPath();
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(long pScalerContext, int glyphCode,
|
||||
float x, float y) {
|
||||
return new GlyphRenderData();
|
||||
}
|
||||
|
||||
long createScalerContext(double[] matrix, int aa,
|
||||
int fm, float boldness, float italic, boolean disableHinting) {
|
||||
return getNullScalerContext();
|
||||
|
||||
@@ -874,6 +874,21 @@ public class StandardGlyphVector extends GlyphVector {
|
||||
return result;
|
||||
}
|
||||
|
||||
public GlyphRenderData getGlyphRenderData(float x, float y) {
|
||||
setFRCTX();
|
||||
initPositions();
|
||||
|
||||
GlyphRenderData result = new GlyphRenderData();
|
||||
for (int i = 0, n = 0; i < glyphs.length; ++i, n += 2) {
|
||||
float px = x + positions[n];
|
||||
float py = y + positions[n+1];
|
||||
|
||||
getGlyphStrike(i).appendGlyphRenderData(glyphs[i], result, px, py);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* !!! not used currently, but might be by getPixelbounds?
|
||||
*/
|
||||
@@ -1845,6 +1860,19 @@ public class StandardGlyphVector extends GlyphVector {
|
||||
PathIterator iterator = gp.getPathIterator(null);
|
||||
result.append(iterator, false);
|
||||
}
|
||||
|
||||
void appendGlyphRenderData(int glyphID, GlyphRenderData result, float x, float y) {
|
||||
// !!! fontStrike needs a method for this. For that matter, GeneralPath does.
|
||||
GlyphRenderData grd;
|
||||
if (sgv.invdtx == null) {
|
||||
grd = strike.getGlyphRenderData(glyphID, x + dx, y + dy);
|
||||
} else {
|
||||
grd = strike.getGlyphRenderData(glyphID, 0, 0);
|
||||
grd.transform(sgv.invdtx);
|
||||
grd.transform(AffineTransform.getTranslateInstance(x + dx, y + dy));
|
||||
}
|
||||
result.merge(grd);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -48,31 +48,32 @@ public class OGLContext extends BufferedContext {
|
||||
private final OGLGraphicsConfig config;
|
||||
|
||||
static {
|
||||
EventQueue.invokeLater(() -> {
|
||||
Integer blitTileSize = AccessController.doPrivileged((PrivilegedAction<Integer>)() ->
|
||||
Integer.parseInt(System.getProperty("sun.java2d.opengl.blitTileSize", "128")));
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
Integer blitTileSize = AccessController.doPrivileged((PrivilegedAction<Integer>) () ->
|
||||
Integer.parseInt(System.getProperty("sun.java2d.opengl.blitTileSize", "128")));
|
||||
|
||||
if (blitTileSize < 0) {
|
||||
int maxDeviceSize = 0;
|
||||
try {
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
if (ge != null) {
|
||||
for (GraphicsDevice gd : ge.getScreenDevices()) {
|
||||
GraphicsConfiguration gc = gd.getDefaultConfiguration();
|
||||
if (gc == null) continue;
|
||||
AffineTransform tx = gc.getDefaultTransform();
|
||||
Rectangle bounds = gc.getBounds();
|
||||
maxDeviceSize = Math.max((int)(bounds.width * tx.getScaleX()), maxDeviceSize);
|
||||
maxDeviceSize = Math.max((int)(bounds.height * tx.getScaleY()), maxDeviceSize);
|
||||
if (blitTileSize < 0) {
|
||||
int maxDeviceSize = 0;
|
||||
try {
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
if (ge != null) {
|
||||
for (GraphicsDevice gd : ge.getScreenDevices()) {
|
||||
GraphicsConfiguration gc = gd.getDefaultConfiguration();
|
||||
if (gc == null) continue;
|
||||
AffineTransform tx = gc.getDefaultTransform();
|
||||
Rectangle bounds = gc.getBounds();
|
||||
maxDeviceSize = Math.max((int) (bounds.width * tx.getScaleX()), maxDeviceSize);
|
||||
maxDeviceSize = Math.max((int) (bounds.height * tx.getScaleY()), maxDeviceSize);
|
||||
}
|
||||
}
|
||||
} catch (HeadlessException ignore) {
|
||||
}
|
||||
blitTileSize = Math.max(128, Integer.highestOneBit((int) (maxDeviceSize * 1.25)));
|
||||
}
|
||||
catch (HeadlessException ignore) {
|
||||
}
|
||||
blitTileSize = Math.max(128, Integer.highestOneBit((int)(maxDeviceSize * 1.25)));
|
||||
}
|
||||
init(blitTileSize);
|
||||
});
|
||||
init(blitTileSize);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static native void init(int blitTileSize);
|
||||
|
||||
@@ -28,12 +28,13 @@ package sun.java2d.pipe;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.font.TextLayout;
|
||||
|
||||
import sun.font.GlyphRenderData;
|
||||
import sun.font.StandardGlyphVector;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.awt.SunHints;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.geom.GeneralPath;
|
||||
|
||||
/**
|
||||
* A delegate pipe of SG2D for drawing "large" text with
|
||||
@@ -78,36 +79,22 @@ public class OutlineTextRenderer implements TextPipe {
|
||||
}
|
||||
TextLayout tl = new TextLayout(str, g2d.getFont(),
|
||||
g2d.getFontRenderContext());
|
||||
Shape s = tl.getOutline(AffineTransform.getTranslateInstance(x, y));
|
||||
|
||||
int textAAHint = g2d.getFontInfo().aaHint;
|
||||
|
||||
int prevaaHint = - 1;
|
||||
if (textAAHint != SunHints.INTVAL_TEXT_ANTIALIAS_OFF &&
|
||||
g2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {
|
||||
prevaaHint = g2d.antialiasHint;
|
||||
g2d.antialiasHint = SunHints.INTVAL_ANTIALIAS_ON;
|
||||
g2d.validatePipe();
|
||||
} else if (textAAHint == SunHints.INTVAL_TEXT_ANTIALIAS_OFF
|
||||
&& g2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_OFF) {
|
||||
prevaaHint = g2d.antialiasHint;
|
||||
g2d.antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
|
||||
g2d.validatePipe();
|
||||
}
|
||||
|
||||
g2d.fill(s);
|
||||
|
||||
if (prevaaHint != -1) {
|
||||
g2d.antialiasHint = prevaaHint;
|
||||
g2d.validatePipe();
|
||||
}
|
||||
// This will end up calling our drawGlyphVector
|
||||
tl.draw(g2d, (float) x, (float) y);
|
||||
}
|
||||
|
||||
public void drawGlyphVector(SunGraphics2D g2d, GlyphVector gv,
|
||||
float x, float y) {
|
||||
|
||||
GlyphRenderData grd;
|
||||
if (gv instanceof StandardGlyphVector) {
|
||||
grd = ((StandardGlyphVector) gv).getGlyphRenderData(x, y);
|
||||
} else {
|
||||
grd = new GlyphRenderData();
|
||||
grd.outline = new GeneralPath(gv.getOutline(x, y));
|
||||
}
|
||||
|
||||
Shape s = gv.getOutline(x, y);
|
||||
int prevaaHint = - 1;
|
||||
FontRenderContext frc = gv.getFontRenderContext();
|
||||
boolean aa = frc.isAntiAliased();
|
||||
@@ -134,7 +121,7 @@ public class OutlineTextRenderer implements TextPipe {
|
||||
g2d.validatePipe();
|
||||
}
|
||||
|
||||
g2d.fill(s);
|
||||
grd.draw(g2d);
|
||||
|
||||
if (prevaaHint != -1) {
|
||||
g2d.antialiasHint = prevaaHint;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -80,6 +80,12 @@ typedef struct FontManagerNativeIDs {
|
||||
/* sun/font/GlyphList */
|
||||
jfieldID glyphListX, glyphListY, glyphListLen,
|
||||
glyphImages, glyphListUsePos, glyphListPos, lcdRGBOrder, lcdSubPixPos;
|
||||
|
||||
/* sun/font/GlyphRenderData */
|
||||
jfieldID glyphRenderDataOutline, glyphRenderDataColorLayers;
|
||||
jmethodID glyphRenderDataSetColorLayersListMID,
|
||||
glyphRenderDataAddColorLayerMID, glyphRenderDataAddColorLayerFGMID,
|
||||
glyphRenderDataAddBitmapMID;
|
||||
} FontManagerNativeIDs;
|
||||
|
||||
/* Note: we share variable in the context of fontmanager lib
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "jvm_md.h"
|
||||
#include "sunfontids.h"
|
||||
#include "sun_font_FreetypeFontScaler.h"
|
||||
|
||||
#include "freetype/tttables.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
@@ -51,6 +51,16 @@
|
||||
#include FT_MODULE_H
|
||||
#include FT_LCD_FILTER_H
|
||||
|
||||
// Linux is built with system Freetype by default,
|
||||
// and it's often a bit old and doesn't have FT_COLOR_H.
|
||||
// Thus, we disable colored outlines on Linux to be able
|
||||
// to build on older Linuxes, this is not a big problem,
|
||||
// as Linux uses bitmap emoji anyway.
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
#include FT_COLOR_H
|
||||
#define ENABLE_COLOR_OUTLINES
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_FONTCONFIG
|
||||
/* Use bundled fontconfig.h for now */
|
||||
#include "fontconfig.h"
|
||||
@@ -81,6 +91,7 @@
|
||||
#define FT26Dot6ToFloat(x) ((x) / ((float) (1<<6)))
|
||||
#define FT26Dot6ToDouble(x) ((x) / ((double) (1<<6)))
|
||||
#define FT26Dot6ToInt(x) (((int)(x)) >> 6)
|
||||
#define FT26Dot6ToIntRound(x) (((int)(x + (1 << 5))) >> 6)
|
||||
#define FT26Dot6ToIntCeil(x) (((int)(x - 1 + (1 << 6))) >> 6)
|
||||
#define IntToFT26Dot6(x) (((FT_Fixed)(x)) << 6)
|
||||
#define DEFAULT_DPI 72
|
||||
@@ -122,6 +133,12 @@ typedef struct CachedMatch {
|
||||
#define NUM_CACHED_VALUES 8
|
||||
#endif
|
||||
|
||||
// Define these manually when building with old Freetype (before 2.5)
|
||||
#if !defined(FT_LOAD_COLOR)
|
||||
#define FT_LOAD_COLOR ( 1L << 20 )
|
||||
#define FT_PIXEL_MODE_BGRA 7
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/* Important note:
|
||||
JNI forbids sharing same env between different threads.
|
||||
@@ -830,7 +847,7 @@ static void setupLoadRenderFlags(FTScalerContext *context, int fcHintStyle, FcBo
|
||||
|
||||
static void setupTransform(FT_Matrix* target, FTScalerContext *context) {
|
||||
FT_Matrix* transform = &context->transform;
|
||||
if (context->doItalize) {
|
||||
if (context->doItalize && !context->colorFont) {
|
||||
// we cannot use FT_GlyphSlot_Oblique as it doesn't work well with arbitrary transforms,
|
||||
// so we add corresponding shear transform to the requested glyph transformation
|
||||
target->xx = FT_MATRIX_ONE;
|
||||
@@ -854,18 +871,18 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
|
||||
scalerInfo->font2D = font2D;
|
||||
|
||||
if (context != NULL) {
|
||||
context->colorFont = FT_HAS_COLOR(scalerInfo->face) || !FT_IS_SCALABLE(scalerInfo->face);
|
||||
|
||||
setupTransform(&matrix, context);
|
||||
FT_Set_Transform(scalerInfo->face, &matrix, NULL);
|
||||
|
||||
FT_UInt dpi = (FT_UInt) getScreenResolution(env);
|
||||
if (FT_IS_SCALABLE(scalerInfo->face)) { // Standard scalable face
|
||||
context->colorFont = FT_HAS_COLOR(scalerInfo->face);
|
||||
context->fixedSizeIndex = -1;
|
||||
errCode = FT_Set_Char_Size(scalerInfo->face, 0,
|
||||
ADJUST_FONT_SIZE(context->ptsz, dpi),
|
||||
dpi, dpi);
|
||||
} else { // Non-scalable face (that should only be bitmap faces)
|
||||
context->colorFont = TRUE;
|
||||
const int ptsz = context->ptsz;
|
||||
// Best size is smallest, but not smaller than requested
|
||||
int bestSizeIndex = 0;
|
||||
@@ -1135,14 +1152,15 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
|
||||
}
|
||||
|
||||
// using same values as for the transformation matrix
|
||||
#define OBLIQUE_MODIFIER(y) (context->doItalize ? ((y)*FT_MATRIX_OBLIQUE_XY/FT_MATRIX_ONE) : 0)
|
||||
#define OBLIQUE_MODIFIER(y) \
|
||||
((context->doItalize && !context->colorFont) ? ((y)*FT_MATRIX_OBLIQUE_XY/FT_MATRIX_ONE) : 0)
|
||||
|
||||
/* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
|
||||
* strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
|
||||
* been taken from libfreetype version 2.6 and remain valid at least up to
|
||||
* 2.9.1. */
|
||||
#define BOLD_MODIFIER(units_per_EM, y_scale) \
|
||||
(context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
|
||||
((context->doBold && !context->colorFont) ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
@@ -1163,6 +1181,7 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
|
||||
(FTScalerInfo*) jlong_to_ptr(pScaler);
|
||||
|
||||
int errCode;
|
||||
jlong ascent, descent, height;
|
||||
|
||||
if (isNullScalerContext(context) || scalerInfo == NULL) {
|
||||
return (*env)->NewObject(env,
|
||||
@@ -1204,6 +1223,19 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
|
||||
FTFixedToFloat(context->transform.yy) * (y))
|
||||
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
#if defined(_WIN32)
|
||||
TT_OS2* info = (TT_OS2*)FT_Get_Sfnt_Table(scalerInfo->face, FT_SFNT_OS2);
|
||||
if (info) {
|
||||
ascent = (jlong) (info->usWinAscent);
|
||||
descent = (jlong) (-info->usWinDescent);
|
||||
height = (jlong) (info->usWinAscent + info->usWinDescent);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ascent = (jlong)scalerInfo->face->ascender;
|
||||
descent = (jlong)scalerInfo->face->descender;
|
||||
height = (jlong) scalerInfo->face->height;
|
||||
}
|
||||
/*
|
||||
* See FreeType source code:
|
||||
* src/base/ftobjs.c ft_recompute_scaled_metrics()
|
||||
@@ -1212,12 +1244,12 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
|
||||
/* ascent */
|
||||
ax = 0;
|
||||
ay = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->ascender),
|
||||
ascent,
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* descent */
|
||||
dx = 0;
|
||||
dy = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->descender),
|
||||
descent,
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* baseline */
|
||||
bx = by = 0;
|
||||
@@ -1225,7 +1257,7 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
|
||||
/* leading */
|
||||
lx = 0;
|
||||
ly = (jfloat) (FT_MulFixFloatShift6(
|
||||
(jlong) scalerInfo->face->height,
|
||||
height,
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale))
|
||||
+ ay - dy;
|
||||
/* max advance */
|
||||
@@ -1278,7 +1310,7 @@ static jlong
|
||||
getGlyphImageNativeInternal(
|
||||
JNIEnv *env, jobject scaler, jobject font2D,
|
||||
jlong pScalerContext, jlong pScaler, jint glyphCode,
|
||||
jboolean renderImage);
|
||||
jboolean renderImage, jboolean setupContext);
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
@@ -1307,7 +1339,7 @@ Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
|
||||
jlong image;
|
||||
|
||||
image = getGlyphImageNativeInternal(
|
||||
env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE);
|
||||
env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE, JNI_TRUE);
|
||||
info = (GlyphInfo*) jlong_to_ptr(image);
|
||||
|
||||
if (info != NULL) {
|
||||
@@ -1333,7 +1365,7 @@ Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
|
||||
|
||||
jlong image = getGlyphImageNativeInternal(
|
||||
env, scaler, font2D,
|
||||
pScalerContext, pScaler, glyphCode, JNI_FALSE);
|
||||
pScalerContext, pScaler, glyphCode, JNI_FALSE, JNI_TRUE);
|
||||
info = (GlyphInfo*) jlong_to_ptr(image);
|
||||
|
||||
if (info != NULL) {
|
||||
@@ -1643,14 +1675,14 @@ Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
|
||||
|
||||
return getGlyphImageNativeInternal(
|
||||
env, scaler, font2D,
|
||||
pScalerContext, pScaler, glyphCode, JNI_TRUE);
|
||||
pScalerContext, pScaler, glyphCode, JNI_TRUE, JNI_TRUE);
|
||||
}
|
||||
|
||||
static jlong
|
||||
getGlyphImageNativeInternal(
|
||||
JNIEnv *env, jobject scaler, jobject font2D,
|
||||
jlong pScalerContext, jlong pScaler, jint glyphCode,
|
||||
jboolean renderImage) {
|
||||
jboolean renderImage, jboolean setupContext) {
|
||||
|
||||
static int PADBYTES = 3;
|
||||
int error, imageSize;
|
||||
@@ -1700,11 +1732,13 @@ static jlong
|
||||
((double)context->ptsz)/64.0);
|
||||
}
|
||||
|
||||
error = setupFTContext(env, font2D, scalerInfo, context, TRUE);
|
||||
if (error) {
|
||||
if (logFFS) fprintf(stderr, "FFS_LOG: Cannot setup FT context\n");
|
||||
invalidateJavaScaler(env, scaler, scalerInfo);
|
||||
return ptr_to_jlong(getNullGlyphImage());
|
||||
if (setupContext) {
|
||||
error = setupFTContext(env, font2D, scalerInfo, context, TRUE);
|
||||
if (error) {
|
||||
if (logFFS) fprintf(stderr, "FFS_LOG: Cannot setup FT context\n");
|
||||
invalidateJavaScaler(env, scaler, scalerInfo);
|
||||
return ptr_to_jlong(getNullGlyphImage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1872,12 +1906,12 @@ static jlong
|
||||
(float) - (advh * FTFixedToFloat(context->transform.yx));
|
||||
} else {
|
||||
if (!ftglyph->advance.y) {
|
||||
glyphInfo->advanceX = (float) FT26Dot6ToInt(
|
||||
glyphInfo->advanceX = FT26Dot6ToIntRound(
|
||||
FT_MulFix(ftglyph->advance.x, manualScale));
|
||||
glyphInfo->advanceY = 0;
|
||||
} else if (!ftglyph->advance.x) {
|
||||
glyphInfo->advanceX = 0;
|
||||
glyphInfo->advanceY = (float) FT26Dot6ToInt(
|
||||
glyphInfo->advanceY = FT26Dot6ToIntRound(
|
||||
-FT_MulFix(ftglyph->advance.y, manualScale));
|
||||
} else {
|
||||
glyphInfo->advanceX = FT26Dot6ToFloat(
|
||||
@@ -2075,24 +2109,13 @@ Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
|
||||
|
||||
#define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
|
||||
|
||||
static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
|
||||
FTScalerContext *context, FTScalerInfo* scalerInfo,
|
||||
jint glyphCode, jfloat xpos, jfloat ypos) {
|
||||
static FT_Outline* getFTOutlineNoSetup(FTScalerContext *context, FTScalerInfo* scalerInfo,
|
||||
jint glyphCode, jfloat xpos, jfloat ypos) {
|
||||
|
||||
FT_Error error;
|
||||
FT_GlyphSlot ftglyph;
|
||||
FT_Int32 loadFlags;
|
||||
|
||||
if (glyphCode >= INVISIBLE_GLYPHS ||
|
||||
isNullScalerContext(context) || scalerInfo == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
error = setupFTContext(env, font2D, scalerInfo, context, TRUE);
|
||||
if (error) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We cannot get an outline from bitmap version of glyph
|
||||
loadFlags = context->loadFlags | FT_LOAD_NO_BITMAP;
|
||||
|
||||
@@ -2102,9 +2125,10 @@ static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
|
||||
}
|
||||
|
||||
ftglyph = scalerInfo->face->glyph;
|
||||
int outlineGlyph = ftglyph->format == FT_GLYPH_FORMAT_OUTLINE;
|
||||
|
||||
/* apply styles */
|
||||
if (context->doBold) { /* if bold style */
|
||||
if (context->doBold && outlineGlyph && !context->colorFont) { /* if bold style */
|
||||
FT_GlyphSlot_Embolden(ftglyph);
|
||||
}
|
||||
|
||||
@@ -2115,6 +2139,24 @@ static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
|
||||
return &ftglyph->outline;
|
||||
}
|
||||
|
||||
static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
|
||||
FTScalerContext *context, FTScalerInfo* scalerInfo,
|
||||
jint glyphCode, jfloat xpos, jfloat ypos) {
|
||||
FT_Error error;
|
||||
|
||||
if (glyphCode >= INVISIBLE_GLYPHS ||
|
||||
isNullScalerContext(context) || scalerInfo == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
error = setupFTContext(env, font2D, scalerInfo, context, TRUE);
|
||||
if (error) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return getFTOutlineNoSetup(context, scalerInfo, glyphCode, xpos, ypos);
|
||||
}
|
||||
|
||||
#define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
|
||||
|
||||
/* Types of GeneralPath segments.
|
||||
@@ -2271,19 +2313,13 @@ static void freeGP(GPData* gpdata) {
|
||||
}
|
||||
}
|
||||
|
||||
static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
|
||||
FTScalerContext *context, FTScalerInfo *scalerInfo,
|
||||
jint glyphCode, jfloat xpos, jfloat ypos) {
|
||||
static jobject outlineToGeneralPath(JNIEnv* env, FT_Outline* outline) {
|
||||
|
||||
FT_Outline* outline;
|
||||
jobject gp = NULL;
|
||||
jbyteArray types;
|
||||
jfloatArray coords;
|
||||
GPData gpdata;
|
||||
|
||||
outline = getFTOutline(env, font2D, context, scalerInfo,
|
||||
glyphCode, xpos, ypos);
|
||||
|
||||
if (outline == NULL || outline->n_points == 0) {
|
||||
return gp;
|
||||
}
|
||||
@@ -2321,6 +2357,75 @@ static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
|
||||
return gp;
|
||||
}
|
||||
|
||||
static jboolean addColorLayersRenderData(JNIEnv* env, FTScalerContext *context,
|
||||
FTScalerInfo* scalerInfo, jint glyphCode,
|
||||
jfloat xpos, jfloat ypos, jobject result) {
|
||||
|
||||
#ifdef ENABLE_COLOR_OUTLINES
|
||||
FT_Error error;
|
||||
|
||||
FT_Color* colors;
|
||||
error = FT_Palette_Select(scalerInfo->face, 0, &colors);
|
||||
if (error) return JNI_FALSE;
|
||||
|
||||
FT_LayerIterator iterator;
|
||||
iterator.p = NULL;
|
||||
FT_UInt glyphIndex, colorIndex;
|
||||
if (!FT_Get_Color_Glyph_Layer(scalerInfo->face, glyphCode,
|
||||
&glyphIndex, &colorIndex, &iterator)) return JNI_FALSE;
|
||||
(*env)->CallVoidMethod(env, result, sunFontIDs.glyphRenderDataSetColorLayersListMID, iterator.num_layers);
|
||||
do {
|
||||
FT_Outline* outline = getFTOutlineNoSetup(context, scalerInfo, glyphIndex, xpos, ypos);
|
||||
jobject gp = outlineToGeneralPath(env, outline);
|
||||
|
||||
if (colorIndex == 0xFFFF) {
|
||||
(*env)->CallVoidMethod(env, result, sunFontIDs.glyphRenderDataAddColorLayerFGMID, gp);
|
||||
} else {
|
||||
(*env)->CallVoidMethod(env, result, sunFontIDs.glyphRenderDataAddColorLayerMID,
|
||||
colors[colorIndex].red, colors[colorIndex].green,
|
||||
colors[colorIndex].blue, colors[colorIndex].alpha, gp);
|
||||
}
|
||||
} while(FT_Get_Color_Glyph_Layer(scalerInfo->face, glyphCode,
|
||||
&glyphIndex, &colorIndex, &iterator));
|
||||
|
||||
return JNI_TRUE;
|
||||
#else
|
||||
return JNI_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void addBitmapRenderData(JNIEnv *env, jobject scaler, jobject font2D,
|
||||
FTScalerContext *context, FTScalerInfo* scalerInfo,
|
||||
jint glyphCode, jfloat xpos, jfloat ypos, jobject result) {
|
||||
GlyphInfo* glyphInfo = (GlyphInfo*) jlong_to_ptr(getGlyphImageNativeInternal(
|
||||
env, scaler, font2D,
|
||||
ptr_to_jlong(context), ptr_to_jlong(scalerInfo),
|
||||
glyphCode, JNI_FALSE, JNI_FALSE));
|
||||
|
||||
FT_GlyphSlot ftglyph = scalerInfo->face->glyph;
|
||||
|
||||
if (ftglyph->bitmap.pixel_mode != FT_PIXEL_MODE_BGRA) return;
|
||||
|
||||
int pitch = ftglyph->bitmap.pitch / 4;
|
||||
int size = pitch * ftglyph->bitmap.rows;
|
||||
jintArray array = (*env)->NewIntArray(env, size);
|
||||
(*env)->SetIntArrayRegion(env, array, 0, size, (jint*) ftglyph->bitmap.buffer);
|
||||
|
||||
double bitmapSize = (double) scalerInfo->face->available_sizes[context->fixedSizeIndex].size;
|
||||
double scale = (double) context->ptsz / bitmapSize / (double) (ftFixed1);
|
||||
double tx = ftglyph->bitmap_left + xpos * bitmapSize / (double) context->ptsz;
|
||||
double ty = -ftglyph->bitmap_top + ypos * bitmapSize / (double) context->ptsz;
|
||||
|
||||
jdouble m00 = (jdouble) context->transform.xx * scale, m10 = (jdouble) context->transform.xy * scale;
|
||||
jdouble m01 = (jdouble) context->transform.yx * scale, m11 = (jdouble) context->transform.yy * scale;
|
||||
jdouble m02 = m00 * tx + m01 * ty, m12 = m10 * tx + m11 * ty;
|
||||
|
||||
free(glyphInfo);
|
||||
(*env)->CallVoidMethod(env, result, sunFontIDs.glyphRenderDataAddBitmapMID,
|
||||
m00, m10, m01, m11, m02, m12,
|
||||
ftglyph->bitmap.width, ftglyph->bitmap.rows, pitch, 2, array);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
* Method: getGlyphOutlineNative
|
||||
@@ -2335,13 +2440,10 @@ Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
|
||||
(FTScalerContext*) jlong_to_ptr(pScalerContext);
|
||||
FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
|
||||
|
||||
jobject gp = getGlyphGeneralPath(env,
|
||||
font2D,
|
||||
context,
|
||||
scalerInfo,
|
||||
glyphCode,
|
||||
xpos,
|
||||
ypos);
|
||||
FT_Outline* outline = getFTOutline(env, font2D, context,
|
||||
scalerInfo, glyphCode,
|
||||
xpos, ypos);
|
||||
jobject gp = outlineToGeneralPath(env, outline);
|
||||
if (gp == NULL) { /* can be legal */
|
||||
gp = (*env)->NewObject(env,
|
||||
sunFontIDs.gpClass,
|
||||
@@ -2489,6 +2591,42 @@ Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
|
||||
return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
* Method: getGlyphRenderDataNative
|
||||
* Signature: (Lsun/font/Font2D;JIFFLsun/font/GlyphRenderData;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_font_FreetypeFontScaler_getGlyphRenderDataNative(
|
||||
JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
|
||||
jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos, jobject result) {
|
||||
|
||||
FTScalerContext *context =
|
||||
(FTScalerContext*) jlong_to_ptr(pScalerContext);
|
||||
FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
|
||||
|
||||
if (glyphCode >= INVISIBLE_GLYPHS ||
|
||||
isNullScalerContext(context) || scalerInfo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FT_Error error = setupFTContext(env, font2D, scalerInfo, context, TRUE);
|
||||
if (error) return;
|
||||
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
if (!context->colorFont ||
|
||||
!addColorLayersRenderData(env, context, scalerInfo, glyphCode, xpos, ypos, result)) {
|
||||
FT_Outline* outline = getFTOutlineNoSetup(context, scalerInfo, glyphCode, xpos, ypos);
|
||||
jobject gp = outlineToGeneralPath(env, outline);
|
||||
if (gp != NULL) {
|
||||
(*env)->SetObjectField(env, result, sunFontIDs.glyphRenderDataOutline, gp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addBitmapRenderData(env, scaler, font2D, context, scalerInfo, glyphCode, xpos, ypos, result);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
|
||||
JNIEnv *env, jobject scaler, jlong pScaler) {
|
||||
|
||||
@@ -189,6 +189,20 @@ static void initFontIDs(JNIEnv *env) {
|
||||
CHECK_NULL(sunFontIDs.lcdSubPixPos =
|
||||
(*env)->GetFieldID(env, tmpClass, "lcdSubPixPos", "Z"));
|
||||
|
||||
CHECK_NULL(tmpClass = (*env)->FindClass(env, "sun/font/GlyphRenderData"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataOutline =
|
||||
(*env)->GetFieldID(env, tmpClass, "outline", "Ljava/awt/geom/GeneralPath;"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataColorLayers =
|
||||
(*env)->GetFieldID(env, tmpClass, "colorLayers", "Ljava/util/List;"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataSetColorLayersListMID =
|
||||
(*env)->GetMethodID(env, tmpClass, "setColorLayersList", "(I)V"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataAddColorLayerMID =
|
||||
(*env)->GetMethodID(env, tmpClass, "addColorLayers", "(IIIILjava/awt/geom/GeneralPath;)V"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataAddColorLayerFGMID =
|
||||
(*env)->GetMethodID(env, tmpClass, "addColorLayers", "(Ljava/awt/geom/GeneralPath;)V"));
|
||||
CHECK_NULL(sunFontIDs.glyphRenderDataAddBitmapMID =
|
||||
(*env)->GetMethodID(env, tmpClass, "addBitmap", "(DDDDDDIIII[I)V"));
|
||||
|
||||
initLCDGammaTables();
|
||||
|
||||
initialisedFontIDs = 1;
|
||||
|
||||
156
src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
Normal file
156
src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* ftcolor.c
|
||||
*
|
||||
* FreeType's glyph color management (body).
|
||||
*
|
||||
* Copyright (C) 2018-2022 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
* modified, and distributed under the terms of the FreeType project
|
||||
* license, LICENSE.TXT. By continuing to use, modify, or distribute
|
||||
* this file you indicate that you have read the license and
|
||||
* understand and accept it fully.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <freetype/internal/ftdebug.h>
|
||||
#include <freetype/internal/sfnt.h>
|
||||
#include <freetype/internal/tttypes.h>
|
||||
#include <freetype/ftcolor.h>
|
||||
|
||||
|
||||
#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
|
||||
|
||||
static
|
||||
const FT_Palette_Data null_palette_data = { 0, NULL, NULL, 0, NULL };
|
||||
|
||||
|
||||
/* documentation is in ftcolor.h */
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Data_Get( FT_Face face,
|
||||
FT_Palette_Data *apalette_data )
|
||||
{
|
||||
if ( !face )
|
||||
return FT_THROW( Invalid_Face_Handle );
|
||||
if ( !apalette_data)
|
||||
return FT_THROW( Invalid_Argument );
|
||||
|
||||
if ( FT_IS_SFNT( face ) )
|
||||
*apalette_data = ( (TT_Face)face )->palette_data;
|
||||
else
|
||||
*apalette_data = null_palette_data;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
||||
/* documentation is in ftcolor.h */
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Select( FT_Face face,
|
||||
FT_UShort palette_index,
|
||||
FT_Color* *apalette )
|
||||
{
|
||||
FT_Error error;
|
||||
|
||||
TT_Face ttface;
|
||||
SFNT_Service sfnt;
|
||||
|
||||
|
||||
if ( !face )
|
||||
return FT_THROW( Invalid_Face_Handle );
|
||||
|
||||
if ( !FT_IS_SFNT( face ) )
|
||||
{
|
||||
if ( apalette )
|
||||
*apalette = NULL;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
ttface = (TT_Face)face;
|
||||
sfnt = (SFNT_Service)ttface->sfnt;
|
||||
|
||||
error = sfnt->set_palette( ttface, palette_index );
|
||||
if ( error )
|
||||
return error;
|
||||
|
||||
ttface->palette_index = palette_index;
|
||||
|
||||
if ( apalette )
|
||||
*apalette = ttface->palette;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
||||
/* documentation is in ftcolor.h */
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Set_Foreground_Color( FT_Face face,
|
||||
FT_Color foreground_color )
|
||||
{
|
||||
TT_Face ttface;
|
||||
|
||||
|
||||
if ( !face )
|
||||
return FT_THROW( Invalid_Face_Handle );
|
||||
|
||||
if ( !FT_IS_SFNT( face ) )
|
||||
return FT_Err_Ok;
|
||||
|
||||
ttface = (TT_Face)face;
|
||||
|
||||
ttface->foreground_color = foreground_color;
|
||||
ttface->have_foreground_color = 1;
|
||||
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Data_Get( FT_Face face,
|
||||
FT_Palette_Data *apalette_data )
|
||||
{
|
||||
FT_UNUSED( face );
|
||||
FT_UNUSED( apalette_data );
|
||||
|
||||
|
||||
return FT_THROW( Unimplemented_Feature );
|
||||
}
|
||||
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Select( FT_Face face,
|
||||
FT_UShort palette_index,
|
||||
FT_Color* *apalette )
|
||||
{
|
||||
FT_UNUSED( face );
|
||||
FT_UNUSED( palette_index );
|
||||
FT_UNUSED( apalette );
|
||||
|
||||
|
||||
return FT_THROW( Unimplemented_Feature );
|
||||
}
|
||||
|
||||
|
||||
FT_EXPORT_DEF( FT_Error )
|
||||
FT_Palette_Set_Foreground_Color( FT_Face face,
|
||||
FT_Color foreground_color )
|
||||
{
|
||||
FT_UNUSED( face );
|
||||
FT_UNUSED( foreground_color );
|
||||
|
||||
|
||||
return FT_THROW( Unimplemented_Feature );
|
||||
}
|
||||
|
||||
#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
|
||||
|
||||
|
||||
/* END */
|
||||
@@ -415,6 +415,10 @@ abstract class XDecoratedPeer extends XWindowPeer {
|
||||
insets_corrected = true;
|
||||
reshape(dimensions, SET_SIZE, false);
|
||||
} else if (xe.get_parent() == root) {
|
||||
if (!isReparented()) {
|
||||
// X server on Windows (e.g. Cygwin/X) does perform a no-op reparenting to the root window
|
||||
return;
|
||||
}
|
||||
configure_seen = false;
|
||||
insets_corrected = false;
|
||||
|
||||
@@ -1225,12 +1229,19 @@ abstract class XDecoratedPeer extends XWindowPeer {
|
||||
Boolean.valueOf(target == focusedWindow));
|
||||
}
|
||||
|
||||
XWindowPeer toFocus = this;
|
||||
|
||||
if (!ENABLE_MODAL_TRANSIENTS_CHAIN && modalBlocker != null) {
|
||||
((XBaseWindow)AWTAccessor.getComponentAccessor().getPeer(modalBlocker)).toFront();
|
||||
return false;
|
||||
toFocus = AWTAccessor.getComponentAccessor().getPeer(modalBlocker);
|
||||
// raising an already top-most window is a no-op, but we perform corresponding
|
||||
// check here to avoid xmonad WM going into an infinite loop - raise request
|
||||
// causes it to refresh internal state and re-send WM_TAKE_FOCUS message
|
||||
if (!((Window)target).isAncestorOf(modalBlocker) && !toFocus.isTopMostWindow()) {
|
||||
toFocus.toFront();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
XWindowPeer toFocus = this;
|
||||
while (toFocus.nextTransientFor != null) {
|
||||
toFocus = toFocus.nextTransientFor;
|
||||
}
|
||||
|
||||
@@ -156,6 +156,95 @@ public class XInputMethod extends X11InputMethod {
|
||||
return peer.getContentWindow();
|
||||
}
|
||||
|
||||
|
||||
static void onXKeyEventFiltering(final boolean isXKeyEventFiltered) {
|
||||
// Fix of JBR-1573, JBR-2444, JBR-4394 (a.k.a. IDEA-246833).
|
||||
// Input method is considered broken if and only if all the following statements are true:
|
||||
// * XFilterEvent have filtered more than filteredEventsThreshold last events of types KeyPress, KeyRelease;
|
||||
// * Input method hasn't been changed (e.g. recreated);
|
||||
// * The input context is not in preedit state (XNPreeditStartCallback has been called but then XNPreeditDoneCallback - hasn't)
|
||||
|
||||
// The functionality is disabled
|
||||
if (BrokenImDetectionContext.EATEN_EVENTS_THRESHOLD < 1) {
|
||||
return;
|
||||
}
|
||||
// Must be called within AWT_LOCK
|
||||
if (!XToolkit.isAWTLockHeldByCurrentThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isXKeyEventFiltered) {
|
||||
final long nativeDataPtr = BrokenImDetectionContext.obtainCurrentXimNativeDataPtr();
|
||||
if (nativeDataPtr == 0) {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
} else {
|
||||
final int isDuringPreediting = BrokenImDetectionContext.isDuringPreediting(nativeDataPtr);
|
||||
if (isDuringPreediting > 0) {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
} else if (isDuringPreediting == 0) {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
} else if (BrokenImDetectionContext.isCurrentXicPassive(nativeDataPtr)) {
|
||||
// Unfortunately for passive XIC (XIMPreeditNothing | XIMStatusNothing) we have no way to get know
|
||||
// whether the XIC is in preediting state or not, so we won't handle this case.
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
} else {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
}
|
||||
|
||||
if (BrokenImDetectionContext.eatenKeyEventsCount > BrokenImDetectionContext.EATEN_EVENTS_THRESHOLD) {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
recreateAllXIC();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BrokenImDetectionContext {
|
||||
static final int EATEN_EVENTS_THRESHOLD;
|
||||
|
||||
static int eatenKeyEventsCount = 0;
|
||||
|
||||
/**
|
||||
* @return pointer to X11InputMethodData
|
||||
*/
|
||||
static native long obtainCurrentXimNativeDataPtr();
|
||||
|
||||
/**
|
||||
* <0 - unknown
|
||||
* >0 - true
|
||||
* 0 - false
|
||||
*/
|
||||
static native int isDuringPreediting(long ximNativeDataPtr);
|
||||
|
||||
static native boolean isCurrentXicPassive(long ximNativeDataPtr);
|
||||
|
||||
|
||||
static {
|
||||
int eatenEventsThresholdInitializer = 7;
|
||||
final String eventsThresholdMode = System.getProperty("recreate.x11.input.method", "true");
|
||||
|
||||
if ("false".equals(eventsThresholdMode)) {
|
||||
eatenEventsThresholdInitializer = 0;
|
||||
} else if (!"true".equals(eventsThresholdMode)) {
|
||||
try {
|
||||
eatenEventsThresholdInitializer = Integer.parseInt(eventsThresholdMode);
|
||||
} catch (NumberFormatException err) {
|
||||
log.warning(
|
||||
"Invalid value of \"recreate.x11.input.method\" system property \"" +
|
||||
eventsThresholdMode +
|
||||
"\". Only \"true\", \"false\" and integer values are supported",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EATEN_EVENTS_THRESHOLD = eatenEventsThresholdInitializer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Native methods
|
||||
*/
|
||||
|
||||
@@ -1030,21 +1030,28 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
|
||||
ev.get_type() == XConstants.KeyPress
|
||||
|| ev.get_type() == XConstants.KeyRelease)) {
|
||||
|
||||
final boolean isKeyEvent = ( (ev.get_type() == XConstants.KeyPress) ||
|
||||
(ev.get_type() == XConstants.KeyRelease) );
|
||||
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
|
||||
keyEventLog.fine("before XFilterEvent:" + ev);
|
||||
}
|
||||
if (XlibWrapper.XFilterEvent(ev.getPData(), w)) {
|
||||
if (isKeyEvent) {
|
||||
XInputMethod.onXKeyEventFiltering(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
|
||||
ev.get_type() == XConstants.KeyPress
|
||||
|| ev.get_type() == XConstants.KeyRelease)) {
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
|
||||
keyEventLog.fine(
|
||||
"after XFilterEvent:" + ev); // IS THIS CORRECT?
|
||||
}
|
||||
|
||||
if (isKeyEvent) {
|
||||
XInputMethod.onXKeyEventFiltering(false);
|
||||
}
|
||||
|
||||
dispatchEvent(ev);
|
||||
// free event data if XGetEventData was called
|
||||
XlibWrapper.XFreeEventData(getDisplay(), ev.pData);
|
||||
|
||||
@@ -1246,7 +1246,11 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
|
||||
@Override
|
||||
void setUserTimeBeforeShowing() {
|
||||
if (winAttr.initialFocus || shouldSuppressWmTakeFocus()) {
|
||||
if (XWM.getWMID() == XWM.KDE2_WM && isSimpleWindow() && ((Window)target).getType() == Window.Type.POPUP) {
|
||||
// Workaround, to suppress blinking of taskbar icon, when hover popup is displayed for a background window
|
||||
setUserTime(XToolkit.getCurrentServerTime(), false);
|
||||
}
|
||||
else if (winAttr.initialFocus || shouldSuppressWmTakeFocus()) {
|
||||
super.setUserTimeBeforeShowing();
|
||||
}
|
||||
else {
|
||||
@@ -2570,4 +2574,31 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
public void updateWindow() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
boolean isTopMostWindow() {
|
||||
long curChild = 0;
|
||||
long curParent = window;
|
||||
while (curParent != 0) {
|
||||
XQueryTree qt = new XQueryTree(curParent);
|
||||
try {
|
||||
if (qt.execute() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (curParent == qt.get_root()) {
|
||||
// children are reported in bottom-to-top order,
|
||||
// so we are checking the last one
|
||||
int nChildren = qt.get_nchildren();
|
||||
return nChildren > 0 && Native.getWindow(qt.get_children(), nChildren - 1) == curChild;
|
||||
} else {
|
||||
// our window could have been re-parented by window manager,
|
||||
// so we should get to the direct child of the root window
|
||||
curChild = curParent;
|
||||
curParent = qt.get_parent();
|
||||
}
|
||||
} finally {
|
||||
qt.dispose();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,8 +364,8 @@ public abstract class X11InputMethod extends X11InputMethodBase {
|
||||
}
|
||||
}
|
||||
|
||||
static void recreateAllXIC() {
|
||||
// NOTE: called from native within AWT_LOCK
|
||||
protected static void recreateAllXIC() {
|
||||
// NOTE: called within AWT_LOCK
|
||||
Map<X11InputMethod, Integer> im2ctxid = new HashMap<>(activeInputMethods.size());
|
||||
for (X11InputMethod im : activeInputMethods) {
|
||||
im2ctxid.put(im, im.releaseXIC());
|
||||
|
||||
@@ -293,4 +293,8 @@ class NativeStrike extends PhysicalStrike {
|
||||
return new GeneralPath();
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
return new GlyphRenderData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class X11TextRenderer extends GlyphListPipe {
|
||||
}
|
||||
}
|
||||
|
||||
native void doDrawGlyphList(long dstData, long xgc,
|
||||
native boolean doDrawGlyphList(long dstData, long xgc,
|
||||
Region clip, GlyphList gl);
|
||||
|
||||
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
|
||||
@@ -77,7 +77,24 @@ public class X11TextRenderer extends GlyphListPipe {
|
||||
Region clip = sg2d.getCompClip();
|
||||
long xgc = x11sd.getRenderGC(clip, SunGraphics2D.COMP_ISCOPY,
|
||||
null, sg2d.pixel);
|
||||
doDrawGlyphList(x11sd.getNativeOps(), xgc, clip, gl);
|
||||
gl.startGlyphIteration();
|
||||
boolean allGlyphsRendered = doDrawGlyphList(x11sd.getNativeOps(), xgc, clip, gl);
|
||||
// There are some color glyphs, which we couldn't draw
|
||||
if (!allGlyphsRendered) {
|
||||
gl.startGlyphIteration();
|
||||
for (int i = 0; i < gl.getNumGlyphs(); i++) {
|
||||
if (gl.isColorGlyph(i)) {
|
||||
int end;
|
||||
for (end = i + 1; end < gl.getNumGlyphs(); end++) {
|
||||
if (!gl.isColorGlyph(end)) break;
|
||||
}
|
||||
sg2d.loops.drawGlyphListColorLoop.
|
||||
DrawGlyphListColor(sg2d, sg2d.surfaceData,
|
||||
gl, i, end);
|
||||
i = end - 1;
|
||||
} else gl.setGlyphIndex(i);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
@@ -88,11 +105,11 @@ public class X11TextRenderer extends GlyphListPipe {
|
||||
}
|
||||
|
||||
public static class Tracer extends X11TextRenderer {
|
||||
void doDrawGlyphList(long dstData, long xgc,
|
||||
boolean doDrawGlyphList(long dstData, long xgc,
|
||||
Region clip, GlyphList gl)
|
||||
{
|
||||
GraphicsPrimitive.tracePrimitive("X11DrawGlyphs");
|
||||
super.doDrawGlyphList(dstData, xgc, clip, gl);
|
||||
return super.doDrawGlyphList(dstData, xgc, clip, gl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ static jboolean checkPixmap(JNIEnv *env, AwtGraphicsConfigDataPtr cData)
|
||||
static void FillBitmap(XImage *theImage,
|
||||
ImageRef *glyphs, jint totalGlyphs,
|
||||
jint clipLeft, jint clipTop,
|
||||
jint clipRight, jint clipBottom)
|
||||
jint clipRight, jint clipBottom, jboolean *allGlyphsRendered)
|
||||
{
|
||||
int glyphCounter;
|
||||
int scan = theImage->bytes_per_line;
|
||||
@@ -124,12 +124,17 @@ static void FillBitmap(XImage *theImage,
|
||||
if (!pixels) {
|
||||
continue;
|
||||
}
|
||||
rowBytes = glyphs[glyphCounter].width;
|
||||
rowBytes = glyphs[glyphCounter].rowBytes;
|
||||
left = glyphs[glyphCounter].x;
|
||||
top = glyphs[glyphCounter].y;
|
||||
width = glyphs[glyphCounter].width;
|
||||
height = glyphs[glyphCounter].height;
|
||||
|
||||
if ((int) rowBytes == width * 4) { // Skip colored glyphs
|
||||
*allGlyphsRendered = JNI_FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if any clipping required, modify parameters now */
|
||||
right = left + width;
|
||||
bottom = top + height;
|
||||
@@ -206,7 +211,7 @@ static void FillBitmap(XImage *theImage,
|
||||
JNIEXPORT void JNICALL
|
||||
AWTDrawGlyphList(JNIEnv *env, jobject xtr,
|
||||
jlong dstData, jlong gc,
|
||||
SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs)
|
||||
SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs, jboolean *allGlyphsRendered)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
GC xgc, theGC;
|
||||
@@ -259,7 +264,7 @@ AWTDrawGlyphList(JNIEnv *env, jobject xtr,
|
||||
FillBitmap(theImage,
|
||||
glyphs,
|
||||
totalGlyphs,
|
||||
cx1, cy1, cx2, cy2);
|
||||
cx1, cy1, cx2, cy2, allGlyphsRendered);
|
||||
|
||||
// NOTE: Since we are tiling around by BM_W, BM_H offsets
|
||||
// and thePixmap is BM_W x BM_H, we do not have to move
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <sun_awt_X11InputMethodBase.h>
|
||||
#include <sun_awt_X11_XInputMethod.h>
|
||||
#include <sun_awt_X11_XInputMethod_BrokenImDetectionContext.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -129,6 +130,10 @@ typedef struct _X11InputMethodData {
|
||||
#endif
|
||||
char *lookup_buf; /* buffer used for XmbLookupString */
|
||||
int lookup_buf_len; /* lookup buffer size in bytes */
|
||||
|
||||
struct {
|
||||
Boolean isBetweenPreeditStartAndPreeditDone;
|
||||
} brokenImDetectionContext;
|
||||
} X11InputMethodData;
|
||||
|
||||
/*
|
||||
@@ -413,6 +418,8 @@ freeX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData)
|
||||
free((void *)pX11IMData->lookup_buf);
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
free((void *)pX11IMData);
|
||||
}
|
||||
|
||||
@@ -1050,6 +1057,8 @@ createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
|
||||
XNResetState, XIMInitialState,
|
||||
NULL);
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
/* Add the global reference object to X11InputMethod to the list. */
|
||||
addToX11InputMethodGRefList(pX11IMData->x11inputmethod);
|
||||
|
||||
@@ -1070,16 +1079,53 @@ createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
|
||||
static int
|
||||
PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditStartCallback\n"); */
|
||||
/* printf("Native: PreeditStartCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv * const env = GetJNIEnv();
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
jobject javaInputMethodGRef = (jobject)client_data;
|
||||
if (!isX11InputMethodGRefInList(javaInputMethodGRef)) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, javaInputMethodGRef);
|
||||
if (pX11IMData == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = True;
|
||||
|
||||
finally:
|
||||
AWT_UNLOCK();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
PreeditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditDoneCallback\n"); */
|
||||
/* printf("Native: PreeditDoneCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv * const env = GetJNIEnv();
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
jobject javaInputMethodGRef = (jobject)client_data;
|
||||
if (!isX11InputMethodGRefInList(javaInputMethodGRef)) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, javaInputMethodGRef);
|
||||
if (pX11IMData == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
finally:
|
||||
AWT_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1092,6 +1138,8 @@ static void
|
||||
PreeditDrawCallback(XIC ic, XPointer client_data,
|
||||
XIMPreeditDrawCallbackStruct *pre_draw)
|
||||
{
|
||||
/* printf("Native: PreeditDrawCallback(%p, %p, %p)\n", ic, client_data, pre_draw); */
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
jmethodID x11imMethodID;
|
||||
@@ -1184,7 +1232,7 @@ PreeditCaretCallback(XIC ic, XPointer client_data,
|
||||
XIMPreeditCaretCallbackStruct *pre_caret)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditCaretCallback\n"); */
|
||||
/* printf("Native: PreeditCaretCallback(%p, %p, %p)\n", ic, client_data, pre_caret); */
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(MACOSX)
|
||||
@@ -1192,14 +1240,14 @@ static void
|
||||
StatusStartCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusStartCallback:\n"); */
|
||||
/*printf("Native: StatusStartCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
}
|
||||
|
||||
static void
|
||||
StatusDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusDoneCallback:\n"); */
|
||||
/*printf("Native: StatusDoneCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
StatusWindow *statusWindow;
|
||||
@@ -1230,7 +1278,7 @@ StatusDrawCallback(XIC ic, XPointer client_data,
|
||||
XIMStatusDrawCallbackStruct *status_draw)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusDrawCallback:\n"); */
|
||||
/*printf("Native: StatusDrawCallback(%p, %p, %p)\n", ic, client_data, status_draw); */
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
StatusWindow *statusWindow;
|
||||
@@ -1283,6 +1331,8 @@ StatusDrawCallback(XIC ic, XPointer client_data,
|
||||
#endif /* __linux__ || MACOSX */
|
||||
|
||||
static void CommitStringCallback(XIC ic, XPointer client_data, XPointer call_data) {
|
||||
/* printf("Native: CommitStringCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
XIMText * text = (XIMText *)call_data;
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
@@ -1523,6 +1573,110 @@ Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: obtainCurrentXimNativeDataPtr
|
||||
* Signature: ()J
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_obtainCurrentXimNativeDataPtr
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
jlong result = 0;
|
||||
|
||||
if (isX11InputMethodGRefInList(currentX11InputMethodInstance)) {
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance);
|
||||
result = ptr_to_jlong(pX11IMData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: isCurrentXicPassive
|
||||
* Signature: (J)Z
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_isCurrentXicPassive
|
||||
(JNIEnv *env, jclass cls, jlong ximNativeDataPtr)
|
||||
{
|
||||
X11InputMethodData * const pX11ImData = (X11InputMethodData *)jlong_to_ptr(ximNativeDataPtr);
|
||||
if (pX11ImData == NULL) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
const jboolean result = (pX11ImData->current_ic == NULL) ? JNI_FALSE
|
||||
: (pX11ImData->current_ic == pX11ImData->ic_passive) ? JNI_TRUE
|
||||
: JNI_FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static XIMPreeditState getPreeditStateOf(XIC xic) {
|
||||
#if defined(__linux__) && defined(_LP64) && !defined(_LITTLE_ENDIAN)
|
||||
// XIMPreeditState value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
|
||||
unsigned int state = XIMPreeditUnKnown;
|
||||
#else
|
||||
XIMPreeditState state = XIMPreeditUnKnown;
|
||||
#endif
|
||||
|
||||
XVaNestedList preeditStateAttr = XVaCreateNestedList(0, XNPreeditState, &state, NULL);
|
||||
if (preeditStateAttr == NULL) {
|
||||
return XIMPreeditUnKnown;
|
||||
}
|
||||
const char * const unsupportedAttrs = XGetICValues(xic, XNPreeditAttributes, preeditStateAttr, NULL);
|
||||
XFree((void *)preeditStateAttr);
|
||||
|
||||
if (unsupportedAttrs != NULL) {
|
||||
return XIMPreeditUnKnown;
|
||||
}
|
||||
|
||||
return (state == XIMPreeditEnable) ? XIMPreeditEnable
|
||||
: (state == XIMPreeditDisable) ? XIMPreeditDisable
|
||||
: XIMPreeditUnKnown;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: isDuringPreediting
|
||||
* Signature: ()I
|
||||
*
|
||||
* Returns the following values:
|
||||
* * >0 in case the IM is in preediting state;
|
||||
* * 0 in case the IM is not in preediting state;
|
||||
* * <0 in case it's unknown whether the IM is in preediting state or not.
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_isDuringPreediting
|
||||
(JNIEnv *env, jclass cls, jlong ximNativeDataPtr)
|
||||
{
|
||||
X11InputMethodData * const pX11ImData = (X11InputMethodData *)jlong_to_ptr(ximNativeDataPtr);
|
||||
if (pX11ImData == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
jint result = -1;
|
||||
|
||||
if (pX11ImData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone) {
|
||||
result = 1;
|
||||
} else if (pX11ImData->current_ic != NULL) {
|
||||
const XIMPreeditState preeditState = getPreeditStateOf(pX11ImData->current_ic);
|
||||
if (preeditState == XIMPreeditEnable) {
|
||||
result = 1;
|
||||
} else if (preeditState == XIMPreeditDisable) {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11InputMethodBase
|
||||
* Method: initIDs
|
||||
|
||||
@@ -656,61 +656,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XWindowEvent
|
||||
XWindowEvent( (Display *) jlong_to_ptr(display), (Window)window, event_mask, (XEvent *) jlong_to_ptr(event_return));
|
||||
}
|
||||
|
||||
static int filteredEventsCount = 0;
|
||||
static int filteredEventsThreshold = -1;
|
||||
static const int DEFAULT_THRESHOLD = 5;
|
||||
#define KeyPressEventType 2
|
||||
#define KeyReleaseEventType 3
|
||||
|
||||
static void checkBrokenInputMethod(XEvent * event, jboolean isEventFiltered) {
|
||||
// Fix for JBR-2444
|
||||
// By default filteredEventsThreshold == 5, you can turn it of with
|
||||
// recreate.x11.input.method=false
|
||||
if (filteredEventsThreshold < 0) {
|
||||
filteredEventsThreshold = 0;
|
||||
|
||||
// read from VM-property
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
jclass systemCls = (*env)->FindClass(env, "java/lang/System");
|
||||
CHECK_NULL(systemCls);
|
||||
jmethodID mid = (*env)->GetStaticMethodID(env, systemCls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
CHECK_NULL(mid);
|
||||
jstring name = (*env)->NewStringUTF(env, "recreate.x11.input.method");
|
||||
CHECK_NULL(name);
|
||||
jstring jvalue = (*env)->CallStaticObjectMethod(env, systemCls, mid, name);
|
||||
if (jvalue != NULL) {
|
||||
const char * utf8string = (*env)->GetStringUTFChars(env, jvalue, NULL);
|
||||
if (utf8string != NULL) {
|
||||
const int parsedVal = atoi(utf8string);
|
||||
if (parsedVal > 0)
|
||||
filteredEventsThreshold = parsedVal;
|
||||
else if (strncmp(utf8string, "false", 5) == 0)
|
||||
filteredEventsThreshold = 0;
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, jvalue, utf8string);
|
||||
} else {
|
||||
filteredEventsThreshold = DEFAULT_THRESHOLD;
|
||||
}
|
||||
(*env)->DeleteLocalRef(env, name);
|
||||
}
|
||||
if (filteredEventsThreshold <= 0)
|
||||
return;
|
||||
|
||||
if (event->type == KeyPressEventType || event->type == KeyReleaseEventType) {
|
||||
if (isEventFiltered) {
|
||||
filteredEventsCount++;
|
||||
} else {
|
||||
filteredEventsCount = 0;
|
||||
}
|
||||
|
||||
if (filteredEventsCount > filteredEventsThreshold) {
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
JNU_CallStaticMethodByName(env, NULL, "sun/awt/X11InputMethod", "recreateAllXIC", "()V");
|
||||
filteredEventsCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XlibWrapper
|
||||
* Method: XFilterEvent
|
||||
@@ -725,8 +670,10 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_XFilterEvent
|
||||
return (jboolean)True;
|
||||
}
|
||||
#endif
|
||||
jboolean isEventFiltered = (jboolean) XFilterEvent((XEvent *) jlong_to_ptr(ptr), (Window) window);
|
||||
checkBrokenInputMethod((XEvent *)jlong_to_ptr(ptr), isEventFiltered);
|
||||
XEvent* const xEvent = (XEvent *)jlong_to_ptr(ptr);
|
||||
|
||||
const jboolean isEventFiltered = (jboolean) XFilterEvent(xEvent, (Window) window);
|
||||
|
||||
return isEventFiltered;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,14 +45,14 @@
|
||||
JNIEXPORT void JNICALL AWTDrawGlyphList
|
||||
(JNIEnv *env, jobject xtr,
|
||||
jlong dstData, jlong gc,
|
||||
SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs);
|
||||
SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs, jboolean *allGlyphsRendered);
|
||||
|
||||
/*
|
||||
* Class: sun_font_X11TextRenderer
|
||||
* Method: doDrawGlyphList
|
||||
* Signature: (Lsun/java2d/SurfaceData;Ljava/awt/Rectangle;ILsun/font/GlyphList;J)V
|
||||
* Signature: (Lsun/java2d/SurfaceData;Ljava/awt/Rectangle;ILsun/font/GlyphList;J)Z
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_font_X11TextRenderer_doDrawGlyphList
|
||||
JNIEXPORT jboolean JNICALL Java_sun_font_X11TextRenderer_doDrawGlyphList
|
||||
(JNIEnv *env, jobject xtr,
|
||||
jlong dstData, jlong xgc, jobject clip,
|
||||
jobject glyphlist)
|
||||
@@ -64,13 +64,15 @@ JNIEXPORT void JNICALL Java_sun_font_X11TextRenderer_doDrawGlyphList
|
||||
|
||||
glyphCount = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
|
||||
if ((gbv = setupBlitVector(env, glyphlist, 0, glyphCount)) == NULL) {
|
||||
return;
|
||||
return JNI_TRUE;
|
||||
}
|
||||
if (!RefineBounds(gbv, &bounds)) {
|
||||
free(gbv);
|
||||
return;
|
||||
return JNI_TRUE;
|
||||
}
|
||||
jboolean allGlyphsRendered = JNI_TRUE;
|
||||
AWTDrawGlyphList(env, xtr, dstData, xgc,
|
||||
&bounds, gbv->glyphs, gbv->numGlyphs);
|
||||
&bounds, gbv->glyphs, gbv->numGlyphs, &allGlyphsRendered);
|
||||
free(gbv);
|
||||
return allGlyphsRendered;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,11 @@ public class NativeFont extends PhysicalFont {
|
||||
return null;
|
||||
}
|
||||
|
||||
public GlyphRenderData getGlyphRenderData(long pScalerContext, int glyphCode,
|
||||
float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
long getGlyphImage(long pScalerContext, int glyphCode) {
|
||||
return 0L;
|
||||
|
||||
@@ -83,4 +83,8 @@ public class NativeStrike extends PhysicalStrike {
|
||||
return null;
|
||||
}
|
||||
|
||||
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -631,11 +631,13 @@ MsgRouting AwtFrame::WmNcMouseDown(WPARAM hitTest, int x, int y, int button) {
|
||||
case HTMAXBUTTON:
|
||||
case HTCLOSE:
|
||||
case HTMENU:
|
||||
RECT rcWindow;
|
||||
GetWindowRect(GetHWnd(), &rcWindow);
|
||||
POINT myPos;
|
||||
myPos.x = x;
|
||||
myPos.y = y;
|
||||
::ScreenToClient(GetHWnd(), &myPos);
|
||||
WmMouseDown(GetButtonMK(button),
|
||||
x - rcWindow.left,
|
||||
y - rcWindow.top,
|
||||
myPos.x,
|
||||
myPos.y,
|
||||
button);
|
||||
return mrConsume;
|
||||
}
|
||||
@@ -680,9 +682,11 @@ MsgRouting AwtFrame::WmNcMouseMove(WPARAM hitTest, int x, int y) {
|
||||
case HTCLOSE:
|
||||
case HTMENU:
|
||||
case HTCAPTION:
|
||||
RECT rcWindow;
|
||||
GetWindowRect(GetHWnd(), &rcWindow);
|
||||
WmMouseMove(0, x - rcWindow.left, y - rcWindow.top);
|
||||
POINT myPos;
|
||||
myPos.x = x;
|
||||
myPos.y = y;
|
||||
::ScreenToClient(GetHWnd(), &myPos);
|
||||
WmMouseMove(0, myPos.x, myPos.y);
|
||||
if (hitTest != HTCAPTION) return mrConsume; // Preserve default window drag for HTCAPTION
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* reserved comment block
|
||||
* DO NOT REMOVE OR ALTER!
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@@ -26,6 +25,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.sun.org.apache.bcel.internal.Const;
|
||||
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
|
||||
|
||||
/**
|
||||
* This class represents the constant pool, i.e., a table of constants, of
|
||||
@@ -37,6 +37,7 @@ import com.sun.org.apache.bcel.internal.Const;
|
||||
|
||||
* @see Constant
|
||||
* @see com.sun.org.apache.bcel.internal.generic.ConstantPoolGen
|
||||
* @LastModified: May 2022
|
||||
*/
|
||||
public class ConstantPool implements Cloneable, Node {
|
||||
|
||||
@@ -222,8 +223,16 @@ public class ConstantPool implements Cloneable, Node {
|
||||
* @throws IOException
|
||||
*/
|
||||
public void dump( final DataOutputStream file ) throws IOException {
|
||||
file.writeShort(constantPool.length);
|
||||
for (int i = 1; i < constantPool.length; i++) {
|
||||
/*
|
||||
* Constants over the size of the constant pool shall not be written out.
|
||||
* This is a redundant measure as the ConstantPoolGen should have already
|
||||
* reported an error back in the situation.
|
||||
*/
|
||||
int size = constantPool.length < ConstantPoolGen.CONSTANT_POOL_SIZE - 1 ?
|
||||
constantPool.length : ConstantPoolGen.CONSTANT_POOL_SIZE - 1;
|
||||
|
||||
file.writeShort(size);
|
||||
for (int i = 1; i < size; i++) {
|
||||
if (constantPool[i] != null) {
|
||||
constantPool[i].dump(file);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@@ -50,10 +50,10 @@ import com.sun.org.apache.bcel.internal.classfile.ConstantUtf8;
|
||||
* JVM and that Double and Long constants need two slots.
|
||||
*
|
||||
* @see Constant
|
||||
* @LastModified: May 2021
|
||||
* @LastModified: May 2022
|
||||
*/
|
||||
public class ConstantPoolGen {
|
||||
|
||||
public static final int CONSTANT_POOL_SIZE = 65536;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 256;
|
||||
private int size;
|
||||
private Constant[] constants;
|
||||
@@ -83,7 +83,7 @@ public class ConstantPoolGen {
|
||||
public ConstantPoolGen(final Constant[] cs) {
|
||||
final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE);
|
||||
|
||||
size = Math.max(DEFAULT_BUFFER_SIZE, cs.length + 64);
|
||||
size = Math.min(Math.max(DEFAULT_BUFFER_SIZE, cs.length + 64), CONSTANT_POOL_SIZE);
|
||||
constants = new Constant[size];
|
||||
|
||||
System.arraycopy(cs, 0, constants, 0, cs.length);
|
||||
@@ -212,9 +212,18 @@ public class ConstantPoolGen {
|
||||
/** Resize internal array of constants.
|
||||
*/
|
||||
protected void adjustSize() {
|
||||
// 3 extra spaces are needed as some entries may take 3 slots
|
||||
if (index + 3 >= CONSTANT_POOL_SIZE) {
|
||||
throw new RuntimeException("The number of constants " + (index + 3)
|
||||
+ " is over the size of the constant pool: "
|
||||
+ (CONSTANT_POOL_SIZE - 1));
|
||||
}
|
||||
|
||||
if (index + 3 >= size) {
|
||||
final Constant[] cs = constants;
|
||||
size *= 2;
|
||||
// the constant array shall not exceed the size of the constant pool
|
||||
size = Math.min(size, CONSTANT_POOL_SIZE);
|
||||
constants = new Constant[size];
|
||||
System.arraycopy(cs, 0, constants, 0, index);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@ import java.awt.font.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
|
||||
import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
|
||||
|
||||
public class NaNTransform {
|
||||
|
||||
private static void testShape(String msg, Shape s) {
|
||||
@@ -55,7 +52,6 @@ public class NaNTransform {
|
||||
Graphics2D g2d = bi.createGraphics();
|
||||
|
||||
g2d.rotate(NaN);
|
||||
g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
Font font = g2d.getFont();
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
/**
|
||||
* @test
|
||||
* bug JBR-4765 TODO: place OpenJDK bug id here
|
||||
* @summary The test verifies that a press {@link java.awt.event.MouseEvent} contains correct modifiers
|
||||
* although the according native mouse event is accompanied by no mouse modifiers
|
||||
* ({@link sun.lwawt.macosx.NSEvent#nsToJavaModifiers} returns 0 inside
|
||||
* {@link sun.lwawt.macosx.CPlatformResponder#handleMouseEvent(int, int, int, int, int, int, int, int)}).
|
||||
* The situation above happens when a user taps (NOT clicks) a trackpad on a M2 MacBooks.
|
||||
* The test emulates the situation via a direct invocation of
|
||||
* {@link sun.lwawt.macosx.CPlatformResponder#handleMouseEvent(int, int, int, int, int, int, int, int)};
|
||||
* unfortunately it's impossible to use {@link java.awt.Robot} because its mouse press events ARE accompanied
|
||||
* by correct modifiers ({@link sun.lwawt.macosx.NSEvent#nsToJavaModifiers} returns correct values).
|
||||
* @author Nikita Provotorov
|
||||
*
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
*
|
||||
* @compile --add-exports java.desktop/sun.lwawt.macosx=ALL-UNNAMED -g MouseMacTouchPressEventModifiers.java
|
||||
* @run main/othervm --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/sun.lwawt=ALL-UNNAMED --add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED MouseMacTouchPressEventModifiers
|
||||
*/
|
||||
|
||||
import sun.lwawt.macosx.CocoaConstants;
|
||||
import sun.lwawt.macosx.LWCToolkit;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
||||
public class MouseMacTouchPressEventModifiers
|
||||
{
|
||||
/**
|
||||
* How it works:
|
||||
* 1. Send a native mouse press to {@code frame} via
|
||||
* {@link sun.lwawt.macosx.CPlatformResponder#handleMouseEvent(int, int, int, int, int, int, int, int)}
|
||||
* (using reflection).
|
||||
* 2. Wait (via {@link Future#get()}) until it generates a usual java MouseEvent
|
||||
* and dispatches it to the MouseListener of the {@code frame}.
|
||||
* 3. Verify the dispatched MouseEvent contains correct modifiers, modifiersEx and button number.
|
||||
* 4. Do all the steps above but for a corresponding mouse release.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// TreeMap to preserve the testing order
|
||||
final var testCases = new TreeMap<>(Map.of(
|
||||
CocoaConstants.kCGMouseButtonLeft, new MouseEventFieldsToTest(MouseEvent.BUTTON1_MASK, MouseEvent.BUTTON1_DOWN_MASK, MouseEvent.BUTTON1),
|
||||
CocoaConstants.kCGMouseButtonRight, new MouseEventFieldsToTest(MouseEvent.BUTTON3_MASK, MouseEvent.BUTTON3_DOWN_MASK, MouseEvent.BUTTON3),
|
||||
CocoaConstants.kCGMouseButtonCenter, new MouseEventFieldsToTest(MouseEvent.BUTTON2_MASK, MouseEvent.BUTTON2_DOWN_MASK, MouseEvent.BUTTON2)
|
||||
));
|
||||
|
||||
SwingUtilities.invokeAndWait(MouseMacTouchPressEventModifiers::createAndShowGUI);
|
||||
|
||||
try {
|
||||
for (var testCase : testCases.entrySet()) {
|
||||
final var fieldsToTest = testCase.getValue();
|
||||
|
||||
final int mouseX = (frame.getWidth() - 1) / 2;
|
||||
final int mouseY = (frame.getHeight() - 1) / 2;
|
||||
|
||||
// press
|
||||
|
||||
MouseEvent event = frame.sendNativeMousePress(
|
||||
0,
|
||||
testCase.getKey(),
|
||||
1,
|
||||
mouseX,
|
||||
mouseY
|
||||
).get(500, TimeUnit.MILLISECONDS);
|
||||
System.out.println("A mouse press turned into: " + event);
|
||||
|
||||
frame.checkInternalErrors();
|
||||
|
||||
checkMouseEvent(event,
|
||||
MouseEvent.MOUSE_PRESSED, fieldsToTest.modifiers, fieldsToTest.pressModifiersEx, fieldsToTest.button);
|
||||
|
||||
// release
|
||||
|
||||
event = frame.sendNativeMouseRelease(
|
||||
0,
|
||||
testCase.getKey(),
|
||||
1,
|
||||
mouseX,
|
||||
mouseY
|
||||
).get(500, TimeUnit.MILLISECONDS);
|
||||
System.out.println("A mouse release turned into: " + event);
|
||||
|
||||
frame.checkInternalErrors();
|
||||
|
||||
checkMouseEvent(event,
|
||||
MouseEvent.MOUSE_RELEASED, fieldsToTest.modifiers, 0, fieldsToTest.button);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(MouseMacTouchPressEventModifiers::disposeGUI);
|
||||
System.out.flush();
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
throw new RuntimeException("Test failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class MouseEventFieldsToTest {
|
||||
public final int modifiers;
|
||||
public final int pressModifiersEx;
|
||||
public final int button;
|
||||
|
||||
MouseEventFieldsToTest(int modifiers, int pressModifiersEx, int button) {
|
||||
this.modifiers = modifiers;
|
||||
this.pressModifiersEx = pressModifiersEx;
|
||||
this.button = button;
|
||||
}
|
||||
}
|
||||
|
||||
private static MyFrame frame;
|
||||
|
||||
private static void createAndShowGUI() {
|
||||
frame = new MyFrame();
|
||||
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
frame.pack();
|
||||
frame.setSize(800, 500);
|
||||
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setAlwaysOnTop(true);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeGUI() {
|
||||
if (frame != null) {
|
||||
frame.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMouseEvent(MouseEvent me,
|
||||
int expectedId, int expectedModifiers, int expectedModifiersEx, int expectedButton
|
||||
) {
|
||||
boolean wrong = false;
|
||||
|
||||
final var errMsg = new StringBuilder(1024);
|
||||
errMsg.append("Wrong MouseEvent ").append(me).append(':');
|
||||
|
||||
if (me.getID() != expectedId) {
|
||||
errMsg.append("\n eventId: expected <").append(expectedId).append(">, actual <").append(me.getID()).append('>');
|
||||
wrong = true;
|
||||
}
|
||||
if (me.getModifiers() != expectedModifiers) {
|
||||
errMsg.append("\n modifiers: expected <").append(expectedModifiers).append(">, actual <").append(me.getModifiers()).append('>');
|
||||
wrong = true;
|
||||
}
|
||||
if (me.getModifiersEx() != expectedModifiersEx) {
|
||||
errMsg.append("\n modifiersEx: expected <").append(expectedModifiersEx).append(">, actual <").append(me.getModifiersEx()).append('>');
|
||||
wrong = true;
|
||||
}
|
||||
if (me.getButton() != expectedButton) {
|
||||
errMsg.append("\n button: expected <").append(expectedButton).append(">, actual <").append(me.getButton()).append('>');
|
||||
wrong = true;
|
||||
}
|
||||
|
||||
if (wrong) {
|
||||
throw new IllegalArgumentException(errMsg.append('\n').toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MyFrame extends JFrame {
|
||||
public MyFrame() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
System.out.println("MyFrame::mousePressed: " + e);
|
||||
keepPromiseVia(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
System.out.println("MyFrame::mouseReleased: " + e);
|
||||
keepPromiseVia(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Future<MouseEvent> sendNativeMousePress(int modifierFlags, int buttonNumber, int clickCount, int x, int y) {
|
||||
final int eventType = (buttonNumber == CocoaConstants.kCGMouseButtonLeft) ? CocoaConstants.NSLeftMouseDown
|
||||
: (buttonNumber == CocoaConstants.kCGMouseButtonRight) ? CocoaConstants.NSRightMouseDown
|
||||
: CocoaConstants.NSOtherMouseDown;
|
||||
|
||||
return sendNativeMouseEvent(eventType, modifierFlags, buttonNumber, clickCount, x, y, getX() + x, getY() + y);
|
||||
}
|
||||
|
||||
public Future<MouseEvent> sendNativeMouseRelease(int modifierFlags, int buttonNumber, int clickCount, int x, int y) {
|
||||
final int eventType = (buttonNumber == CocoaConstants.kCGMouseButtonLeft) ? CocoaConstants.NSLeftMouseUp
|
||||
: (buttonNumber == CocoaConstants.kCGMouseButtonRight) ? CocoaConstants.NSRightMouseUp
|
||||
: CocoaConstants.NSOtherMouseUp;
|
||||
|
||||
return sendNativeMouseEvent(eventType, modifierFlags, buttonNumber, clickCount, x, y, getX() + x, getY() + y);
|
||||
}
|
||||
|
||||
public void checkInternalErrors() throws Throwable {
|
||||
final Throwable result = internalError.getAndSet(null);
|
||||
if (result != null) {
|
||||
throw result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final AtomicReference<CompletableFuture<MouseEvent>> mouseEventPromise = new AtomicReference<>(null);
|
||||
|
||||
private final AtomicReference<Throwable> internalError = new AtomicReference<>(null);
|
||||
|
||||
private Future<MouseEvent> sendNativeMouseEvent(
|
||||
final int eventType,
|
||||
final int modifierFlags,
|
||||
final int buttonNumber,
|
||||
final int clickCount,
|
||||
final int x,
|
||||
final int y,
|
||||
final int absX,
|
||||
final int absY
|
||||
) {
|
||||
assert !SwingUtilities.isEventDispatchThread();
|
||||
assert mouseEventPromise.get() == null : "Trying to send a mouse event while there is already a processing one";
|
||||
|
||||
final CompletableFuture<MouseEvent> result = new CompletableFuture<>();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
LWCToolkit.invokeLater(() -> {
|
||||
try {
|
||||
final Object thisPlatformResponder = obtainFramePlatformResponder(this);
|
||||
final Method thisPlatformResponderHandleMouseEventMethod = obtainHandleMouseEventMethod(thisPlatformResponder);
|
||||
|
||||
if (mouseEventPromise.compareAndExchange(null, result) != null) {
|
||||
throw new IllegalStateException("Trying to send a mouse event while there is already a processing one");
|
||||
}
|
||||
|
||||
thisPlatformResponderHandleMouseEventMethod.invoke(thisPlatformResponder,
|
||||
eventType, modifierFlags, buttonNumber, clickCount, x, y, absX, absY);
|
||||
} catch (Throwable err) {
|
||||
// Remove the promise if thisPlatformResponderHandleMouseEventMethod.invoke(...) failed
|
||||
mouseEventPromise.compareAndExchange(result, null);
|
||||
failPromiseDueTo(result, err);
|
||||
}
|
||||
}, this);
|
||||
} catch (Throwable err) {
|
||||
failPromiseDueTo(result, err);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Wraps {@link CompletableFuture#complete(Object)} */
|
||||
private void keepPromiseVia(MouseEvent mouseEvent) {
|
||||
try {
|
||||
final CompletableFuture<MouseEvent> promise = mouseEventPromise.getAndSet(null);
|
||||
if (promise == null) {
|
||||
throw new IllegalStateException("The following unexpected MouseEvent has arrived: " + mouseEvent);
|
||||
}
|
||||
|
||||
if (!promise.complete(mouseEvent)) {
|
||||
throw new IllegalStateException("The promise had already been completed when the following MouseEvent arrived: " + mouseEvent);
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
setInternalError(err);
|
||||
}
|
||||
}
|
||||
|
||||
/** Wraps {@link CompletableFuture#completeExceptionally(Throwable)} */
|
||||
private void failPromiseDueTo(CompletableFuture<MouseEvent> promise, Throwable cause) {
|
||||
try {
|
||||
if (!promise.completeExceptionally(cause)) {
|
||||
throw new IllegalStateException("The promise had already been completed when the following error arrived: " + cause);
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
setInternalError(err);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInternalError(Throwable err) {
|
||||
if (internalError.compareAndExchange(null, err) != null) {
|
||||
System.err.println("Failed to set the following internal error because there is another one: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtains {@code component.peer.platformWindow.responder} */
|
||||
private static Object obtainFramePlatformResponder(Component component) throws NoSuchFieldException, IllegalAccessException {
|
||||
final Object framePeer;
|
||||
{
|
||||
final var frameClass = Component.class;
|
||||
final var peerField = frameClass.getDeclaredField("peer");
|
||||
|
||||
peerField.setAccessible(true);
|
||||
|
||||
framePeer = peerField.get(component);
|
||||
}
|
||||
|
||||
final Object peerPlatformWindow;
|
||||
{
|
||||
final var peerClass = framePeer.getClass();
|
||||
final var platformWindowField = peerClass.getDeclaredField("platformWindow");
|
||||
|
||||
platformWindowField.setAccessible(true);
|
||||
|
||||
peerPlatformWindow = platformWindowField.get(framePeer);
|
||||
}
|
||||
|
||||
final Object platformWindowResponder;
|
||||
{
|
||||
final var peerPlatformWindowClass = peerPlatformWindow.getClass();
|
||||
final var platformWindowResponderField = peerPlatformWindowClass.getDeclaredField("responder");
|
||||
|
||||
platformWindowResponderField.setAccessible(true);
|
||||
|
||||
platformWindowResponder = platformWindowResponderField.get(peerPlatformWindow);
|
||||
}
|
||||
|
||||
return platformWindowResponder;
|
||||
}
|
||||
|
||||
/** Obtains {@link sun.lwawt.macosx.CPlatformResponder#handleMouseEvent(int, int, int, int, int, int, int, int)} */
|
||||
private static Method obtainHandleMouseEventMethod(final Object platformResponder) throws NoSuchMethodException {
|
||||
final var responderClass = platformResponder.getClass();
|
||||
final var handleMouseEventMethod = responderClass.getDeclaredMethod(
|
||||
"handleMouseEvent",
|
||||
int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class
|
||||
);
|
||||
|
||||
handleMouseEventMethod.setAccessible(true);
|
||||
|
||||
return handleMouseEventMethod;
|
||||
}
|
||||
}
|
||||
125
test/jdk/java/awt/font/ComplexEmoji.java
Normal file
125
test/jdk/java/awt/font/ComplexEmoji.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Checks that complex emoji are rendered with proper shaping.
|
||||
*/
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ComplexEmoji {
|
||||
private static final int IMG_WIDTH = 60;
|
||||
private static final int IMG_HEIGHT = 20;
|
||||
|
||||
private static final String[] EMOJI = {
|
||||
"\ud83d\udd25", // Fire
|
||||
"\u2764\ufe0f", // Heart + color variation selector
|
||||
"\ud83e\udd18\ud83c\udffb", // Horns sign - white hand
|
||||
"\ud83d\udc41\ufe0f\u200d\ud83d\udde8\ufe0f", // Eye in speech bubble - ZWJ sequence
|
||||
"\uD83C\uDDE6\uD83C\uDDF6", // Antarctica flag
|
||||
"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f", // England flag - tag sequence
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
requireFont("Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji");
|
||||
|
||||
// Platform-specific tricks
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
EMOJI[4] = EMOJI[5] = null; // Flags and tags are not supported on Windows
|
||||
}
|
||||
|
||||
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||
String errors = "";
|
||||
for (int i = 0; i < EMOJI.length; i++) {
|
||||
String emoji = EMOJI[i];
|
||||
if (emoji == null) continue;
|
||||
drawEmoji(img, emoji);
|
||||
String error = checkEmoji(img);
|
||||
if (error != null) {
|
||||
errors += "\n#" + i + ": " + error;
|
||||
try {
|
||||
ImageIO.write(img, "PNG", new File("ComplexEmoji" + i + ".png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) throw new RuntimeException(errors);
|
||||
}
|
||||
|
||||
private static void drawEmoji(Image img, String emoji) {
|
||||
Graphics g = img.getGraphics();
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
|
||||
g.setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
|
||||
// Try to trick shaper by prepending "A" letter
|
||||
// White on white will not be visible anyway
|
||||
g.drawString("A" + emoji, 2, 15);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
private static String checkEmoji(BufferedImage img) {
|
||||
Point min = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
Point max = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
|
||||
for (int x = 0; x < IMG_WIDTH; x++) {
|
||||
for (int y = 0; y < IMG_HEIGHT; y++) {
|
||||
int rgb = img.getRGB(x, y);
|
||||
if (rgb != -1) {
|
||||
if (x < min.x) min.x = x;
|
||||
if (y < min.y) min.y = y;
|
||||
if (x > max.x) max.x = x;
|
||||
if (y > max.y) max.y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (min.x >= max.x || min.y >= max.y) {
|
||||
return "Empty image";
|
||||
}
|
||||
int width = max.x - min.x + 1;
|
||||
int height = max.y - min.y + 1;
|
||||
double ratio = (double) width / (double) height;
|
||||
if (ratio > 1.5) {
|
||||
return "Too wide image, is there few glyphs instead of one?";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void requireFont(String macOS, String windows, String linux) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String font;
|
||||
if (os.contains("mac")) font = macOS;
|
||||
else if (os.contains("windows")) font = windows;
|
||||
else if (os.contains("linux")) font = linux;
|
||||
else return;
|
||||
String[] fs = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||
if (Stream.of(fs).noneMatch(s -> s.equals(font))) {
|
||||
throw new Error("Required font not found: " + font);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
test/jdk/java/awt/font/EmojiVariation.java
Normal file
178
test/jdk/java/awt/font/EmojiVariation.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Checks that variation selectors work.
|
||||
*/
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
|
||||
import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
|
||||
|
||||
public class EmojiVariation {
|
||||
private static final int IMG_WIDTH = 100;
|
||||
private static final int IMG_HEIGHT = 50;
|
||||
|
||||
private static final Color SYMBOL_COLOR = Color.MAGENTA;
|
||||
|
||||
// These emoji must be monochrome by default
|
||||
private static final String[] SYMBOLS = {
|
||||
"\u0023","\u002a","\u0030","\u0031","\u0032","\u0033","\u0034","\u0035","\u0036","\u0037","\u0038","\u0039",
|
||||
"\u00a9","\u00ae","\u203c","\u2049","\u2122","\u2139","\u2194","\u2195","\u2196","\u2197","\u2198","\u2199",
|
||||
"\u21a9","\u21aa","\u2328","\u23cf","\u23ed","\u23ee","\u23ef","\u23f1","\u23f2","\u23f8","\u23f9","\u23fa",
|
||||
"\u24c2","\u25aa","\u25ab","\u25b6","\u25c0","\u25fb","\u25fc","\u2600","\u2601","\u2602","\u2603","\u2604",
|
||||
"\u260e","\u2611","\u2618","\u261d","\u2620","\u2622","\u2623","\u2626","\u262a","\u262e","\u262f","\u2638",
|
||||
"\u2639","\u263a","\u2640","\u2642","\u265f","\u2660","\u2663","\u2665","\u2666","\u2668","\u267b","\u267e",
|
||||
"\u2692","\u2694","\u2695","\u2696","\u2697","\u2699","\u269b","\u269c","\u26a0","\u26b0","\u26b1","\u26c8",
|
||||
"\u26cf","\u26d1","\u26d3","\u26e9","\u26f0","\u26f1","\u26f4","\u26f7","\u26f8","\u26f9","\u2702","\u2708",
|
||||
"\u2709","\u270c","\u270d","\u270f","\u2712","\u2714","\u2716","\u271d","\u2721","\u2733","\u2734","\u2744",
|
||||
"\u2747","\u2763","\u2764","\u27a1","\u2934","\u2935","\u2b05","\u2b06","\u2b07","\u3030","\u303d","\u3297",
|
||||
"\u3299","\ud83c\udd70","\ud83c\udd71","\ud83c\udd7e","\ud83c\udd7f","\ud83c\ude02","\ud83c\ude37",
|
||||
"\ud83c\udf21","\ud83c\udf24","\ud83c\udf25","\ud83c\udf26","\ud83c\udf27","\ud83c\udf28","\ud83c\udf29",
|
||||
"\ud83c\udf2a","\ud83c\udf2b","\ud83c\udf2c","\ud83c\udf36","\ud83c\udf7d","\ud83c\udf96","\ud83c\udf97",
|
||||
"\ud83c\udf99","\ud83c\udf9a","\ud83c\udf9b","\ud83c\udf9e","\ud83c\udf9f","\ud83c\udfcb","\ud83c\udfcc",
|
||||
"\ud83c\udfcd","\ud83c\udfce","\ud83c\udfd4","\ud83c\udfd5","\ud83c\udfd6","\ud83c\udfd7","\ud83c\udfd8",
|
||||
"\ud83c\udfd9","\ud83c\udfda","\ud83c\udfdb","\ud83c\udfdc","\ud83c\udfdd","\ud83c\udfde","\ud83c\udfdf",
|
||||
"\ud83c\udff3","\ud83c\udff5","\ud83c\udff7","\ud83d\udc3f","\ud83d\udc41","\ud83d\udcfd","\ud83d\udd49",
|
||||
"\ud83d\udd4a","\ud83d\udd6f","\ud83d\udd70","\ud83d\udd73","\ud83d\udd74","\ud83d\udd75","\ud83d\udd76",
|
||||
"\ud83d\udd77","\ud83d\udd78","\ud83d\udd79","\ud83d\udd87","\ud83d\udd8a","\ud83d\udd8b","\ud83d\udd8c",
|
||||
"\ud83d\udd8d","\ud83d\udd90","\ud83d\udda5","\ud83d\udda8","\ud83d\uddb1","\ud83d\uddb2","\ud83d\uddbc",
|
||||
"\ud83d\uddc2","\ud83d\uddc3","\ud83d\uddc4","\ud83d\uddd1","\ud83d\uddd2","\ud83d\uddd3","\ud83d\udddc",
|
||||
"\ud83d\udddd","\ud83d\uddde","\ud83d\udde1","\ud83d\udde3","\ud83d\udde8","\ud83d\uddef","\ud83d\uddf3",
|
||||
"\ud83d\uddfa","\ud83d\udecb","\ud83d\udecd","\ud83d\udece","\ud83d\udecf","\ud83d\udee0","\ud83d\udee1",
|
||||
"\ud83d\udee2","\ud83d\udee3","\ud83d\udee4","\ud83d\udee5","\ud83d\udee9","\ud83d\udef0","\ud83d\udef3",
|
||||
};
|
||||
|
||||
private enum Variation {
|
||||
DEFAULT(false, ""),
|
||||
MONO(false, "\ufe0e"),
|
||||
COLOR(true, "\ufe0f");
|
||||
|
||||
final boolean colorByDefault;
|
||||
final String suffix;
|
||||
|
||||
Variation(boolean colorByDefault, String suffix) {
|
||||
this.colorByDefault = colorByDefault;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
requireFont("Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji");
|
||||
requireFont("Zapf Dingbats", "Segoe UI Symbol", "DejaVu Sans");
|
||||
|
||||
// Platform-specific tricks
|
||||
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||
// Many emoji on Linux don't have monochrome variants
|
||||
Arrays.fill(SYMBOLS, 28, 37, null);
|
||||
Arrays.fill(SYMBOLS, 83, 94, null);
|
||||
Arrays.fill(SYMBOLS, 117, SYMBOLS.length, null);
|
||||
} else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
|
||||
// Many emoji on macOS don't have monochrome variants
|
||||
Arrays.fill(SYMBOLS, 28, 36, null);
|
||||
Arrays.fill(SYMBOLS, 81, 94, null);
|
||||
Arrays.fill(SYMBOLS, 127, SYMBOLS.length, null);
|
||||
}
|
||||
|
||||
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||
String errors = "";
|
||||
for (String s : SYMBOLS) {
|
||||
if (s == null) continue;
|
||||
errors += test(img, s, Variation.DEFAULT);
|
||||
errors += test(img, s, Variation.MONO);
|
||||
errors += test(img, s, Variation.COLOR);
|
||||
}
|
||||
if (!errors.isEmpty()) throw new RuntimeException(errors);
|
||||
}
|
||||
|
||||
private static String test(BufferedImage img, String symbol, Variation variation) {
|
||||
draw(img, symbol + variation.suffix);
|
||||
String error = check(img, variation.colorByDefault);
|
||||
if (error != null) {
|
||||
String name = symbol.chars().mapToObj(c -> {
|
||||
String s = Integer.toHexString(c);
|
||||
return "0".repeat(4 - s.length()) + s;
|
||||
}).collect(Collectors.joining("-")) + "-" + variation;
|
||||
try {
|
||||
ImageIO.write(img, "PNG", new File("EmojiVariation-" + name + ".png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "\n" + name + ": " + error;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static void draw(Image img, String symbol) {
|
||||
Graphics2D g = (Graphics2D) img.getGraphics();
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
|
||||
g.setFont(new Font(Font.DIALOG, Font.PLAIN, 50));
|
||||
g.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_OFF);
|
||||
g.setColor(SYMBOL_COLOR);
|
||||
g.drawString(symbol, 2, 42);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
private static String check(BufferedImage img, boolean expectColor) {
|
||||
boolean rendered = false;
|
||||
boolean color = false;
|
||||
for (int x = 0; x < IMG_WIDTH; x++) {
|
||||
for (int y = 0; y < IMG_HEIGHT; y++) {
|
||||
int rgb = img.getRGB(x, y);
|
||||
if (rgb != Color.white.getRGB()) {
|
||||
rendered = true;
|
||||
if (rgb != SYMBOL_COLOR.getRGB()) color = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!rendered) {
|
||||
return "Empty image";
|
||||
} else if (color != expectColor) {
|
||||
return expectColor ? "Expected color but rendered mono" : "Expected mono but rendered color";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void requireFont(String macOS, String windows, String linux) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String font;
|
||||
if (os.contains("mac")) font = macOS;
|
||||
else if (os.contains("windows")) font = windows;
|
||||
else if (os.contains("linux")) font = linux;
|
||||
else return;
|
||||
String[] fs = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||
if (Stream.of(fs).noneMatch(s -> s.equals(font))) {
|
||||
throw new Error("Required font not found: " + font);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
test/jdk/java/awt/font/OutlineTextRendererEmoji.java
Normal file
186
test/jdk/java/awt/font/OutlineTextRendererEmoji.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Checks that emoji rendered via glyph cache and bypassing it look similar.
|
||||
*/
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
|
||||
import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
|
||||
|
||||
public class OutlineTextRendererEmoji {
|
||||
private static final int IMG_WIDTH = 84;
|
||||
private static final int IMG_HEIGHT = 84;
|
||||
private static final int EMOJI_X = 0;
|
||||
private static final int EMOJI_Y = 70;
|
||||
private static final int FONT_SIZE = 70;
|
||||
private static final String EMOJI = "\ud83d\udd25"; // Fire
|
||||
|
||||
private static final int WINDOW_SIZE = 12; // In pixels
|
||||
private static final double THRESHOLD = 0.98;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
requireFont("Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji");
|
||||
|
||||
BufferedImage small = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||
BufferedImage rescaled = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||
BufferedImage big = new BufferedImage(IMG_WIDTH*2, IMG_HEIGHT*2, BufferedImage.TYPE_INT_RGB);
|
||||
drawEmoji(small, EMOJI_X, EMOJI_Y, FONT_SIZE);
|
||||
drawEmoji(big, EMOJI_X*2, EMOJI_Y*2, FONT_SIZE*2);
|
||||
checkEmoji(small, big, rescaled);
|
||||
}
|
||||
|
||||
private static void drawEmoji(Image img, int x, int y, int size) {
|
||||
Graphics2D g = (Graphics2D) img.getGraphics();
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0, 0, img.getWidth(null), img.getHeight(null));
|
||||
g.setFont(new Font(Font.DIALOG, Font.PLAIN, size));
|
||||
g.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
|
||||
g.drawString(EMOJI, x, y);
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
private static void checkEmoji(BufferedImage small, BufferedImage big, BufferedImage rescaled) throws Exception {
|
||||
Graphics2D g2d = rescaled.createGraphics();
|
||||
g2d.drawImage(big.getScaledInstance(small.getWidth(), small.getHeight(), Image.SCALE_SMOOTH), 0, 0, null);
|
||||
g2d.dispose();
|
||||
|
||||
double ssim = SSIM.calculate(small, rescaled, WINDOW_SIZE);
|
||||
System.out.println("SSIM is " + ssim);
|
||||
|
||||
if (ssim < THRESHOLD) {
|
||||
ImageIO.write(small, "PNG", new File("OutlineTextRendererEmoji-small.png"));
|
||||
ImageIO.write(big, "PNG", new File("OutlineTextRendererEmoji-big.png"));
|
||||
ImageIO.write(rescaled, "PNG", new File("OutlineTextRendererEmoji-rescaled.png"));
|
||||
throw new Exception("Images mismatch: " + ssim);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SSIM {
|
||||
private static double calculate(BufferedImage a, BufferedImage b, int windowSize) {
|
||||
if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
|
||||
throw new IllegalArgumentException("Images must have same size");
|
||||
}
|
||||
if (a.getWidth() % windowSize != 0 || a.getHeight() % windowSize != 0) {
|
||||
throw new IllegalArgumentException("Image sizes must be multiple of windowSize");
|
||||
}
|
||||
|
||||
final double K1 = 0.01, K2 = 0.03;
|
||||
final double L = 255; // dynamic range per component (2^8 - 1)
|
||||
final double c1 = Math.pow(L * K1, 2);
|
||||
final double c2 = Math.pow(L * K2, 2);
|
||||
|
||||
double result = 0, alpha = 0;
|
||||
int windows = 0;
|
||||
for (int y = 0; y <= a.getHeight() - windowSize; y++) {
|
||||
for (int x = 0; x <= a.getWidth() - windowSize; x++) {
|
||||
|
||||
// Calculate averages
|
||||
double[] avgA = vec(), avgB = vec();
|
||||
for (int py = 0; py < windowSize; py++) {
|
||||
for (int px = 0; px < windowSize; px++) {
|
||||
avgA = add(avgA, vec(a.getRGB(x + px, y + py)));
|
||||
avgB = add(avgB, vec(b.getRGB(x + px, y + py)));
|
||||
}
|
||||
}
|
||||
avgA = div(avgA, windowSize * windowSize);
|
||||
avgB = div(avgB, windowSize * windowSize);
|
||||
|
||||
// Calculate variance and covariance
|
||||
double[] varA = vec(), varB = vec(), cov = vec();
|
||||
for (int py = 0; py < windowSize; py++) {
|
||||
for (int px = 0; px < windowSize; px++) {
|
||||
double[] da = sub(avgA, vec(a.getRGB(x + px, y + py)));
|
||||
double[] db = sub(avgB, vec(b.getRGB(x + px, y + py)));
|
||||
varA = add(varA, mul(da, da));
|
||||
varB = add(varB, mul(db, db));
|
||||
cov = add(cov, mul(da, db));
|
||||
}
|
||||
}
|
||||
varA = div(varA, windowSize * windowSize);
|
||||
varB = div(varB, windowSize * windowSize);
|
||||
cov = div(cov, windowSize * windowSize);
|
||||
|
||||
// Calculate ssim
|
||||
double[] ssim = vec();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ssim[i] = (
|
||||
(2 * avgA[i] * avgB[i] + c1) * (2 * cov[i] + c2)
|
||||
) / (
|
||||
(avgA[i]*avgA[i] + avgB[i]*avgB[i] + c1) * (varA[i] + varB[i] + c2)
|
||||
);
|
||||
}
|
||||
|
||||
result += ssim[0] + ssim[1] + ssim[2];
|
||||
alpha += ssim[3];
|
||||
windows++;
|
||||
}
|
||||
}
|
||||
if (alpha == windows) result /= 3.0;
|
||||
else result = (result + alpha) / 4.0;
|
||||
return result / (double) windows;
|
||||
}
|
||||
|
||||
private static double[] vec(double... v) {
|
||||
if (v.length == 0) return new double[4];
|
||||
else if (v.length == 1) return new double[] {v[0],v[0],v[0],v[0]};
|
||||
else return v;
|
||||
}
|
||||
private static double[] vec(int color) {
|
||||
return vec(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff);
|
||||
}
|
||||
|
||||
interface Op { double apply(double a, double b); }
|
||||
private static double[] apply(Op op, double[] a, double... b) {
|
||||
b = vec(b);
|
||||
double[] r = new double[4];
|
||||
for (int i = 0; i < 4; i++) r[i] = op.apply(a[i], b[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
private static double[] add(double[] a, double... b) { return apply((i, j) -> i + j, a, b); }
|
||||
private static double[] sub(double[] a, double... b) { return apply((i, j) -> i - j, a, b); }
|
||||
private static double[] mul(double[] a, double... b) { return apply((i, j) -> i * j, a, b); }
|
||||
private static double[] div(double[] a, double... b) { return apply((i, j) -> i / j, a, b); }
|
||||
}
|
||||
|
||||
private static void requireFont(String macOS, String windows, String linux) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String font;
|
||||
if (os.contains("mac")) font = macOS;
|
||||
else if (os.contains("windows")) font = windows;
|
||||
else if (os.contains("linux")) font = linux;
|
||||
else return;
|
||||
String[] fs = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||
if (Stream.of(fs).noneMatch(s -> s.equals(font))) {
|
||||
throw new Error("Required font not found: " + font);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
test/jdk/java/nio/file/DirectoryStream/ParallelHeavy.java
Executable file
109
test/jdk/java/nio/file/DirectoryStream/ParallelHeavy.java
Executable file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @summary Verifies that DirectoryStream's iterator works correctly in
|
||||
* a multi-threaded environment.
|
||||
* @library ..
|
||||
* @run main/timeout=240 ParallelHeavy
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.DirectoryIteratorException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ParallelHeavy {
|
||||
static final int RETRIES = 42;
|
||||
static final int COUNT_TASKS = 7;
|
||||
static final int COUNT_FILES = 1024;
|
||||
static final int COUNT_DIRECTORIES = 1024;
|
||||
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
final Path testDir = Paths.get(System.getProperty("test.dir", "."));
|
||||
final Path dir = Files.createTempDirectory(testDir, "ParallelHeavy");
|
||||
|
||||
populateDir(dir);
|
||||
|
||||
for (int i = 0; i < RETRIES; i++) {
|
||||
runTest(dir, COUNT_DIRECTORIES + COUNT_FILES - i);
|
||||
}
|
||||
}
|
||||
|
||||
static void advanceIteratorThenCancel(final DirectoryStream<Path> stream, final Iterator<Path> iterator, int closeAfterReading) {
|
||||
while (closeAfterReading-- > 0) {
|
||||
try {
|
||||
iterator.next();
|
||||
} catch (NoSuchElementException e) {
|
||||
System.out.println("NoSuchElementException, stream may have been closed, remaining to read "
|
||||
+ closeAfterReading + ", thread " + Thread.currentThread().getId());
|
||||
break;
|
||||
}
|
||||
Thread.yield();
|
||||
}
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
static void runTest(final Path dir, int closeAfterReading) throws IOException {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
final Iterator<Path> streamIterator = stream.iterator();
|
||||
|
||||
final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
try {
|
||||
for (int i = 0; i < COUNT_TASKS - 1; i++) {
|
||||
pool.submit(() -> advanceIteratorThenCancel(stream, streamIterator, closeAfterReading));
|
||||
}
|
||||
pool.submit(() -> advanceIteratorThenCancel(stream, streamIterator, COUNT_FILES));
|
||||
try {
|
||||
pool.awaitTermination(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {}
|
||||
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
} catch (DirectoryIteratorException ex) {
|
||||
throw ex.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
static void populateDir(final Path root) throws IOException {
|
||||
for (int i = 0; i < COUNT_DIRECTORIES; i++) {
|
||||
final Path newDir = root.resolve("directory-number-" + i);
|
||||
Files.createDirectory(newDir);
|
||||
}
|
||||
|
||||
for (int i = 0; i < COUNT_FILES; i++) {
|
||||
final Path newFile = root.resolve("longer-file-name-number-" + i);
|
||||
Files.createFile(newFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -26,52 +26,83 @@
|
||||
* @bug 4511556
|
||||
* @summary Verify BitString value containing padding bits is accepted.
|
||||
* @modules java.base/sun.security.util
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import sun.security.util.BitArray;
|
||||
import sun.security.util.DerInputStream;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
|
||||
public class PaddedBitString {
|
||||
|
||||
// Relaxed the BitString parsing routine to accept bit strings
|
||||
// with padding bits, ex. treat DER_BITSTRING_PAD6 as the same
|
||||
// bit string as DER_BITSTRING_NOPAD.
|
||||
// with padding bits, ex. treat DER_BITSTRING_PAD6_b as the same
|
||||
// bit string as DER_BITSTRING_PAD6_0/DER_BITSTRING_NOPAD.
|
||||
// Note:
|
||||
// 1. the number of padding bits has to be in [0...7]
|
||||
// 2. value of the padding bits is ignored
|
||||
|
||||
// bit string (01011101 11000000)
|
||||
// With 6 padding bits (01011101 11001011)
|
||||
private final static byte[] DER_BITSTRING_PAD6 = { 3, 3, 6,
|
||||
(byte)0x5d, (byte)0xcb };
|
||||
|
||||
// With no padding bits
|
||||
private final static byte[] DER_BITSTRING_NOPAD = { 3, 3, 0,
|
||||
(byte)0x5d, (byte)0xc0 };
|
||||
// With 6 zero padding bits (01011101 11000000)
|
||||
private final static byte[] DER_BITSTRING_PAD6_0 = { 3, 3, 6,
|
||||
(byte)0x5d, (byte)0xc0 };
|
||||
|
||||
// With 6 nonzero padding bits (01011101 11001011)
|
||||
private final static byte[] DER_BITSTRING_PAD6_b = { 3, 3, 6,
|
||||
(byte)0x5d, (byte)0xcb };
|
||||
|
||||
// With 8 padding bits
|
||||
private final static byte[] DER_BITSTRING_PAD8_0 = { 3, 3, 8,
|
||||
(byte)0x5d, (byte)0xc0 };
|
||||
|
||||
private final static byte[] BITS = { (byte)0x5d, (byte)0xc0 };
|
||||
|
||||
static enum Type {
|
||||
BIT_STRING,
|
||||
UNALIGNED_BIT_STRING;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
byte[] ba0, ba1;
|
||||
try {
|
||||
DerInputStream derin = new DerInputStream(DER_BITSTRING_PAD6);
|
||||
ba1 = derin.getBitString();
|
||||
} catch( IOException e ) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("Unable to parse BitString with 6 padding bits");
|
||||
}
|
||||
test(DER_BITSTRING_NOPAD, new BitArray(16, BITS));
|
||||
test(DER_BITSTRING_PAD6_0, new BitArray(10, BITS));
|
||||
test(DER_BITSTRING_PAD6_b, new BitArray(10, BITS));
|
||||
test(DER_BITSTRING_PAD8_0, null);
|
||||
System.out.println("Tests Passed");
|
||||
}
|
||||
|
||||
try {
|
||||
DerInputStream derin = new DerInputStream(DER_BITSTRING_NOPAD);
|
||||
ba0 = derin.getBitString();
|
||||
} catch( IOException e ) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("Unable to parse BitString with no padding");
|
||||
}
|
||||
|
||||
if (Arrays.equals(ba1, ba0) == false ) {
|
||||
throw new Exception("BitString comparison check failed");
|
||||
private static void test(byte[] in, BitArray ans) throws IOException {
|
||||
System.out.print("Testing ");
|
||||
new HexDumpEncoder().encodeBuffer(in, System.out);
|
||||
for (Type t : Type.values()) {
|
||||
DerInputStream derin = new DerInputStream(in);
|
||||
boolean shouldPass = (ans != null);
|
||||
switch (t) {
|
||||
case BIT_STRING:
|
||||
if (shouldPass) {
|
||||
Asserts.assertTrue(Arrays.equals(ans.toByteArray(),
|
||||
derin.getBitString()));
|
||||
} else {
|
||||
Utils.runAndCheckException(() -> derin.getBitString(),
|
||||
IOException.class);
|
||||
}
|
||||
break;
|
||||
case UNALIGNED_BIT_STRING:
|
||||
if (shouldPass) {
|
||||
Asserts.assertEQ(ans, derin.getUnalignedBitString());
|
||||
} else {
|
||||
Utils.runAndCheckException(() ->
|
||||
derin.getUnalignedBitString(), IOException.class);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user