Compare commits

..

42 Commits

Author SHA1 Message Date
Nikita Provotorov
5e6fa333bf JBR-4765 Cannot invoke context menu by two fingers tapping on MacBook with M2 chip
- Ensure the pressed mouse buttons are also reflected by the appropriate modifiers;
- Add a regression test.
2022-09-05 07:20:42 +03:00
Nikita Gubarkov
e1a4c7a5ba JBR-4466 fix mouse event coordinates for non-client events with custom window decorations 2022-08-10 14:37:15 +03:00
Nikita Gubarkov
f61d6c31a0 Fix italic and bold styles for colored outline glyphs 2022-08-04 12:30:48 +03:00
Nikita Gubarkov
f111d35015 JBR-4686 Fix rotated text metrics 2022-08-01 13:51:57 +03:00
Nikita Gubarkov
84e17bb70f Disable colored outlines on Linux to be able to build with old system Freetype 2022-07-23 11:53:54 +03:00
Nikita Gubarkov
eae3965ef9 Fix compilation 2022-07-21 15:17:03 +03:00
Nikita Gubarkov
92aaf88110 Implement rendering of colored outlines and bitmap glyphs in OutlineTextRenderer
Pick changes from OpenJDK review
2022-07-21 14:03:14 +03:00
Dmitry Batrak
f4414aed91 JBR-4652 With multiple projects open non-fullscreen, right-click on Dock icon to select the project from the context menu doesn't switch to that project
(cherry picked from commit e34677587d)
2022-07-21 09:37:09 +03:00
Andrew John Hughes
52b60c5df1 8272243: Improve DER parsing
Backport-of: ec91d4bce10cd965e8a0099f35aed8c9150f6a44
Co-authored-by: Alexey Bakhtin <abakhtin@openjdk.org>
Reviewed-by: mbalao
(cherry picked from commit edd32ef60b)
2022-07-21 10:55:40 +07:00
Sergey Bylokhov serb
b9fcf08391 8284370: Improve zlib usage
Reviewed-by: andrew
Backport-of: 46f42c72a92ffb035e84eb8e73bd6329ef006251
(cherry picked from commit 07dd941ea5)
2022-07-21 10:55:39 +07:00
Martin Balao
83760527fb 8281866: Enhance MethodHandle invocations
Reviewed-by: mbaesken
Backport-of: d974d9da365f787f67971d88c79371c8b0769f75
(cherry picked from commit 132745902a)
2022-07-21 10:55:39 +07:00
Aleksei Voitylov
9091cd940a 8285407: Improve Xalan supports
Reviewed-by: mbalao
Backport-of: fd6385d8c20379c1139f64f5c90d331ad9631097
(cherry picked from commit 13bf52c8d8)
2022-07-21 10:55:39 +07:00
Aleksei Voitylov
eb878643c1 8283190: Improve MIDI processing
Reviewed-by: mbalao, mbaesken
Backport-of: e0329eb343661edd5066deb6ae5d99a742135831
(cherry picked from commit 6bc1874e1e)
2022-07-21 10:55:38 +07:00
Tobias Hartmann
37c85e9c97 8281859: Improve class compilation
Reviewed-by: mbaesken
Backport-of: 3ac62a66efd05d0842076dd4cfbea0e53b12630f
(cherry picked from commit 6c0ba0785a)
2022-07-21 10:55:38 +07:00
Yuri Nesterenko
f0cd40871b 8272249: Better properties of loaded Properties
Reviewed-by: mbalao, mbaesken
Backport-of: f9f7e5eaf51cd4793805e50f8ba3549689d939e9
(cherry picked from commit f7a521ec95)
2022-07-21 10:55:38 +07:00
Aleksei Voitylov
a237e88170 8277608: Address IP Addressing
Reviewed-by: mbalao
Backport-of: f2136e833d5bbeb9eb4e61e73774c36ff7d27cfb
(cherry picked from commit de08dc4d79)
2022-07-21 10:55:37 +07:00
Goetz Lindenmaier
77a19ab692 8282501: Bump update version for OpenJDK: jdk-11.0.16
Reviewed-by: mdoerr
(cherry picked from commit 16d98b354c)
2022-07-21 10:55:10 +07:00
Alexey Ushakov
e1e540783d JBR-3843 IDE text is misaligned vertically when using Consolas font
Use usWinAscent/usWinDescent for metrics on Windows
2022-07-19 20:11:37 +03:00
Dmitry Batrak
31e244239a JBR-4642 regression: "focus follows mouse" broken for modals, I need to click into them
a better solution, fixing also the case when mouse hovers over a window, which is not the modal dialog's ancestor

(cherry picked from commit ec748f84fb)
2022-07-18 17:16:56 +03:00
Dmitry Batrak
fd6cf30685 JBR-4642 regression: "focus follows mouse" broken for modals, I need to click into them
(cherry picked from commit c55bf03680)
2022-07-18 17:16:56 +03:00
Dmitry Batrak
a3e2c24bc2 JBR-4638 Regression: Unable to enter emoji in editor via Emoji & Symbols on macOS
(cherry picked from commit 8b9a00915d)
2022-07-12 17:19:08 +03:00
Nikita Gubarkov
d8fbfe250a JBR-2523 Fix emoji, ZWJ and font fallback
Require layout for some emoji-related unicodes
Fix variation selectors and ZWJ
2022-07-09 13:09:35 +03:00
Nikita Gubarkov
cf825d4972 JBR-2917 Move dingbats and symbol fonts to fallback sequence on Windows
That gives emoji font higher priority which fixes ZWJ sequences in composite fonts.
2022-07-04 23:34:32 +03:00
Maxim Kartashev
2995252420 JBR-3996 javax/swing/reliability/HangDuringStaticInitialization.java fails on Linux in headless mode
Do not invoke Java_sun_java2d_opengl_OGLContext_init() when in the
headless mode because that function isn't compiled into
libawt_headless.so
2022-07-01 12:47:12 +03:00
Nikita Gubarkov
fb0ab92a85 JBR-4373 Add mapping for .NewYork-* and .SFArabic-* system fonts 2022-06-27 23:25:24 +03:00
Nikita Gubarkov
8582a4bb93 JBR-2917 Add Segoe UI Emoji to font fallback on Windows 2022-06-27 22:59:55 +03:00
Nikita Gubarkov
830839072a JBR-3995 Backport font-metrics related code 2022-06-26 20:14:51 +03:00
Maxim Kartashev
ab61c11d26 fixup! JBR-4485 Windows: EXCEPTION_ACCESS_VIOLATION at sun.nio.fs.WindowsDirectoryStream$WindowsDirectoryIterator.readNextEntry 2022-06-21 09:18:51 +03:00
Maxim Kartashev
2d21dade9b JBR-3101 Exception in NSApplicationAWT: java.lang.NullPointerException at java.desktop/sun.lwawt.macosx.CPlatformComponent.setBounds
CWarningWindow doesn't have a peer, so accomodated for that in
CPlatformComponent.setBounds().
2022-06-16 12:29:23 +03:00
Maxim Kartashev
f17faf67eb JBR-4485 Windows: EXCEPTION_ACCESS_VIOLATION at sun.nio.fs.WindowsDirectoryStream$WindowsDirectoryIterator.readNextEntry
As a precaution, range-check the offset returned by WinAPI before
using it to read raw memory with Unsafe.
2022-06-15 10:53:13 +03:00
Dmitry Batrak
e401519a6b JBR-4552 Windows revert back after initial move/resize in Cygwin/X
(cherry picked from commit 6dc194e089)
2022-06-08 15:15:48 +03:00
Dmitry Batrak
877dcd6577 JBR-4543 NPE: IdeEventQueue.lambda$getNextEvent$0 2022-06-06 16:54:29 +03:00
Nikita Provotorov
edae94bfa3 fixup! JBR-4394, IDEA-246833: refactoring and blocking the fix of JBR-1573 to recreate IMs and ICs during preediting. 2022-06-06 23:08:02 +10:00
Nikita Provotorov
5a2ae3b313 JBR-4394, IDEA-246833: refactoring and blocking the fix of JBR-1573 to recreate IMs and ICs during preediting. 2022-06-05 07:00:08 +10:00
Nikita Provotorov
ef262dc8bb JBR-4394, IDEA-246833: fixup of JBR-2444. 2022-06-03 19:28:24 +07:00
Dmitry Batrak
e0e8d694d1 JBR-4537 Popup windows shown for a background window cause app icon to blink in taskbar on KDE 2022-06-03 17:01:21 +03:00
Nikita Gubarkov
fd03016da2 JBR-4031 add null-check for tagsArray returned from CTFontCopyAvailableTables 2022-06-01 00:40:20 +03:00
Vitaly Provodin
bd2b313993 Update README.md
removed the link to JBR for IDEA 2022.2
2022-05-24 10:52:12 +07:00
Nikita Provotorov
b89b770f4f JBR-2074 Windows 10 AArch64 support: synchronize windows/scripts/synchronize/mkimages_aarch64.sh with windows/scripts/synchronize/mkimages_x64.sh. 2022-05-22 21:01:38 +07:00
Nikita Provotorov
100ac45f1c JBR-2074 Windows 10 AArch64 support: synchronize windows/scripts/synchronize/pack_aarch64.sh with windows/scripts/synchronize/pack_x64.sh. 2022-05-22 20:28:42 +07:00
Nikita Provotorov
cd09c3f936 fixup! JBR-3906 JBR for Linux aarch64 with JCEF is missing, is there any support plan? 2022-05-22 19:34:59 +07:00
Vladimir Kempik
f998adfa8f JBR-4452: Update crash report message with JBR youtrack link
instead of bugreport.java.com
2022-05-20 12:11:00 +03:00
91 changed files with 3439 additions and 594 deletions

View File

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

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

View File

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

View File

@@ -0,0 +1 @@
--with-vendor-vm-bug-url=https://youtrack.jetbrains.com/issues/JBR

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)) {

View File

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

View File

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

View File

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

View File

@@ -83,4 +83,8 @@ public class NativeStrike extends PhysicalStrike {
return null;
}
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -293,4 +293,8 @@ class NativeStrike extends PhysicalStrike {
return new GeneralPath();
}
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return new GlyphRenderData();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -83,4 +83,8 @@ public class NativeStrike extends PhysicalStrike {
return null;
}
GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return null;
}
}

View File

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

View File

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

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

View File

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

View File

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

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

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

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

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

View File

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