feat: add IMGUI_USE_WCHAR32 support, cross-compile natives, publish to mvn.mrlokop.dev
CI / Build Java (push) Has been cancelled
CI / Build Native (macos-latest - macos) (push) Has been cancelled
CI / Build Native (ubuntu-latest - linux) (push) Has been cancelled
CI / Build Native (ubuntu-latest - windows) (push) Has been cancelled
CI / Archive Natives (push) Has been cancelled
CI / Update Binaries (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Build Java (push) Has been cancelled
CI / Build Native (macos-latest - macos) (push) Has been cancelled
CI / Build Native (ubuntu-latest - linux) (push) Has been cancelled
CI / Build Native (ubuntu-latest - windows) (push) Has been cancelled
CI / Archive Natives (push) Has been cancelled
CI / Update Binaries (push) Has been cancelled
CI / Release (push) Has been cancelled
- Enable IMGUI_USE_WCHAR32 in imconfig.h (ImWchar = uint32_t) - Extend ImFontGlyphRangesBuilder, ImFontConfig, ImFontAtlas for 32-bit codepoints - Cross-compile natives for macOS universal, Linux x86_64 (glibc), Windows x86_64 - Publish as org.nnstd.imgui:...:1.90.0-wchar32+mrlokop2 to mvn.mrlokop.dev - Add BUILDING.md with full rebuild and publish instructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
2fcd517b3e
commit
34c40304f6
17 files changed
+439
-55
No files matched your search
+230
@@ -0,0 +1,230 @@
|
|||||||
|
# Building imgui-java WCHAR32 Fork
|
||||||
|
|
||||||
|
This fork adds `IMGUI_USE_WCHAR32=1` support so that ImGui can handle Unicode codepoints above U+FFFF (emoji, CJK extensions, etc.). Published as `org.nnstd.imgui:imgui-java-*:1.90.0-wchar32+mrlokopN`.
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
- **Maven**: `https://mvn.mrlokop.dev/releases`
|
||||||
|
- **Git**: `https://git.mrlokop.dev/shlepa/imgui-java`
|
||||||
|
|
||||||
|
## Coordinates
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<repository>
|
||||||
|
<id>mrlokop</id>
|
||||||
|
<url>https://mvn.mrlokop.dev/releases</url>
|
||||||
|
</repository>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-binding</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-lwjgl3</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Pick one natives artifact for your platform: -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-natives-macos</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-natives-linux</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-natives-windows</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Or use the fat jar (includes all natives + LWJGL3): -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nnstd.imgui</groupId>
|
||||||
|
<artifactId>imgui-java-app</artifactId>
|
||||||
|
<version>1.90.0-wchar32+mrlokop2</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
For Gradle (Kotlin DSL):
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
repositories {
|
||||||
|
maven("https://mvn.mrlokop.dev/releases")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.nnstd.imgui:imgui-java-binding:1.90.0-wchar32+mrlokop2")
|
||||||
|
implementation("org.nnstd.imgui:imgui-java-lwjgl3:1.90.0-wchar32+mrlokop2")
|
||||||
|
runtimeOnly("org.nnstd.imgui:imgui-java-natives-linux:1.90.0-wchar32+mrlokop2")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
| Version | Linux toolchain | Notes |
|
||||||
|
|---------|-----------------|-------|
|
||||||
|
| `+mrlokop1` | musl (`x86_64-unknown-linux-musl`) | No glibc dependency; static stdlib |
|
||||||
|
| `+mrlokop2` | glibc (`x86_64-unknown-linux-gnu`) | Standard Linux distros; links libc.so.6 |
|
||||||
|
|
||||||
|
## Prerequisites (macOS build host)
|
||||||
|
|
||||||
|
All cross-compilation is done from a macOS machine.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Java 17 (build toolchain)
|
||||||
|
sdk install java 17.0.18.1-sem # via SDKMAN
|
||||||
|
|
||||||
|
# Native build tools
|
||||||
|
brew install ant
|
||||||
|
brew install mingw-w64 # Windows cross-compiler
|
||||||
|
|
||||||
|
# Linux glibc cross-compiler (for +mrlokop2 / recommended)
|
||||||
|
brew tap messense/macos-cross-toolchains
|
||||||
|
brew install x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
# Linux musl cross-compiler (for +mrlokop1 / no-glibc builds)
|
||||||
|
brew install x86_64-unknown-linux-musl
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Steps
|
||||||
|
|
||||||
|
### 1. Clone and initialise submodules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.mrlokop.dev/shlepa/imgui-java
|
||||||
|
cd imgui-java
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Build vendored FreeType for each platform
|
||||||
|
|
||||||
|
FreeType must be cross-compiled before the JNI natives.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
buildSrc/scripts/vendor_freetype.sh macos
|
||||||
|
buildSrc/scripts/vendor_freetype.sh linux
|
||||||
|
buildSrc/scripts/vendor_freetype.sh windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Each invocation drops a static `libfreetype.a` into `build/vendor/freetype/lib/`. The macOS build uses `lipo` to create a universal (arm64 + x86_64) library automatically.
|
||||||
|
|
||||||
|
### 3. Build JNI natives
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=/path/to/java17
|
||||||
|
|
||||||
|
# macOS universal (arm64 + x86_64)
|
||||||
|
JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh macos
|
||||||
|
|
||||||
|
# Linux x86_64 (glibc)
|
||||||
|
JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh linux
|
||||||
|
|
||||||
|
# Windows x86_64 (mingw)
|
||||||
|
JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiled natives land in:
|
||||||
|
- `bin/macos64/libimgui-java64.dylib`
|
||||||
|
- `bin/macosarm64/libimgui-java64.dylib`
|
||||||
|
- `bin/linux64/libimgui-java64.so`
|
||||||
|
- `bin/windows64/imgui-java64.dll`
|
||||||
|
|
||||||
|
### 4. Bump the version (when releasing a new patch)
|
||||||
|
|
||||||
|
Edit `build.gradle` (root), increment the `+mrlokopN` suffix:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
version = '1.90.0-wchar32+mrlokop3' // was +mrlokop2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Publish to Maven
|
||||||
|
|
||||||
|
Publish each platform's natives separately because the artifact classifier differs. Run all four publish commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JH=/path/to/java17
|
||||||
|
GW="JAVA_HOME=$JH ./gradlew"
|
||||||
|
|
||||||
|
# Binding + LWJGL3 (platform-independent JARs)
|
||||||
|
$GW imgui-binding:publishImguiPublicationToMrLokopRepository \
|
||||||
|
imgui-lwjgl3:publishImguiPublicationToMrLokopRepository
|
||||||
|
|
||||||
|
# Native artifacts (one per platform)
|
||||||
|
$GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=macos
|
||||||
|
$GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=linux
|
||||||
|
$GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=windows
|
||||||
|
|
||||||
|
# Fat app JAR (all natives bundled)
|
||||||
|
$GW imgui-app:publishImguiPublicationToMrLokopRepository
|
||||||
|
```
|
||||||
|
|
||||||
|
## Switching Linux Toolchain (musl vs glibc)
|
||||||
|
|
||||||
|
**glibc** (default, `+mrlokop2`) — standard Linux distributions:
|
||||||
|
|
||||||
|
In `buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy`:
|
||||||
|
```groovy
|
||||||
|
linux64.compilerPrefix = 'x86_64-unknown-linux-gnu-'
|
||||||
|
```
|
||||||
|
|
||||||
|
In `buildSrc/scripts/vendor_freetype.sh`:
|
||||||
|
```bash
|
||||||
|
linux) build_freetype "-fPIC" "--host=x86_64-unknown-linux-gnu" "lib/libfreetype.a" "x86_64-unknown-linux-gnu-gcc" ;;
|
||||||
|
```
|
||||||
|
|
||||||
|
**musl** (`+mrlokop1`) — no glibc dependency, portable:
|
||||||
|
|
||||||
|
In `GenerateLibs.groovy`:
|
||||||
|
```groovy
|
||||||
|
linux64.compilerPrefix = 'x86_64-unknown-linux-musl-'
|
||||||
|
linux64.linkerFlags = linux64.linkerFlags.replace('-Wl,-wrap,memcpy', '')
|
||||||
|
// After AntScriptGenerator.generate(), neutralise memcpy_wrap.c:
|
||||||
|
def memcpyWrap = new File("$jniDir/memcpy_wrap.c")
|
||||||
|
if (memcpyWrap.exists()) {
|
||||||
|
memcpyWrap.text = '/* disabled for musl */'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In `vendor_freetype.sh`:
|
||||||
|
```bash
|
||||||
|
linux) build_freetype "-fPIC" "--host=x86_64-unknown-linux-musl" "lib/libfreetype.a" "x86_64-unknown-linux-musl-gcc" ;;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Changes vs Upstream
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `imgui-binding/src/main/native/imconfig.h` | Uncommented `#define IMGUI_USE_WCHAR32` |
|
||||||
|
| `ImFontGlyphRangesBuilder.java` | `UNICODE_CODEPOINT_MAX=0x10FFFF`; `addChar(int)`; 32-bit LE encoding in `addRanges`/`buildRanges` |
|
||||||
|
| `ImFontConfig.java` | Added `setGlyphRanges(int[])` overload |
|
||||||
|
| `ImFontAtlas.java` | `RETURN_GLYPH_2_SHORT` macro uses `sizeof(ImWchar)/sizeof(jshort)` multiplier |
|
||||||
|
| `build.gradle` | Version hardcoded to `1.90.0-wchar32+mrlokopN` |
|
||||||
|
| `publish.gradle` | `groupId=org.nnstd.imgui`, repo=`https://mvn.mrlokop.dev/releases` |
|
||||||
|
| `GenerateLibs.groovy` | Linux uses `x86_64-unknown-linux-gnu-` prefix |
|
||||||
|
| `vendor_freetype.sh` | `--host=` flags for all platforms; macOS universal via `lipo` |
|
||||||
|
|
||||||
|
## 32-bit Glyph Range Encoding
|
||||||
|
|
||||||
|
With `IMGUI_USE_WCHAR32`, `ImWchar` is `uint32_t` (4 bytes). The Java binding stores glyph ranges as `short[]` where each 32-bit codepoint is packed as two little-endian `jshort`s:
|
||||||
|
|
||||||
|
```
|
||||||
|
ranges = [lo_lo, lo_hi, hi_lo, hi_hi, ..., 0, 0]
|
||||||
|
^--- pair 1 ---^ ^--- pair 2+ ---^ terminator
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `ImFontGlyphRangesBuilder.buildRanges()` or `ImFontConfig.setGlyphRanges(int[])` to handle this automatically:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// int[] variant (simplest)
|
||||||
|
fontConfig.setGlyphRanges(new int[]{ 0x1F000, 0x1F9FF, 0 });
|
||||||
|
|
||||||
|
// Builder variant
|
||||||
|
ImFontGlyphRangesBuilder builder = new ImFontGlyphRangesBuilder();
|
||||||
|
builder.addText("Hello 🌍");
|
||||||
|
short[] ranges = builder.buildRanges();
|
||||||
|
fontConfig.setGlyphRanges(ranges);
|
||||||
|
```
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -8,7 +8,7 @@ ext {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = 'imgui-java'
|
group = 'imgui-java'
|
||||||
version = 'git describe --tags --always'.execute().text.trim().substring(1)
|
version = '1.90.0-wchar32+mrlokop2'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@@ -35,16 +35,23 @@ echo "FreeType unzipped to $LIBDIR"
|
|||||||
COMMON_FLAGS="--enable-static --disable-shared --without-zlib --without-bzip2 --without-png --without-harfbuzz --without-brotli"
|
COMMON_FLAGS="--enable-static --disable-shared --without-zlib --without-bzip2 --without-png --without-harfbuzz --without-brotli"
|
||||||
|
|
||||||
# Function to configure and build FreeType
|
# Function to configure and build FreeType
|
||||||
|
# Args: cflags prefix output_dir [cc]
|
||||||
build_freetype() {
|
build_freetype() {
|
||||||
cflags=$1
|
cflags=$1
|
||||||
prefix=$2
|
prefix=$2
|
||||||
output_dir=$3
|
output_dir=$3
|
||||||
|
cc=${4:-}
|
||||||
|
|
||||||
echo "Cleaning previous builds..."
|
echo "Cleaning previous builds..."
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
echo "Configuring FreeType with CFLAGS='$cflags' and PREFIX='$prefix'..."
|
if [ -n "$cc" ]; then
|
||||||
./configure CFLAGS="$cflags" $COMMON_FLAGS $prefix
|
echo "Configuring FreeType with CC='$cc' CFLAGS='$cflags' and PREFIX='$prefix'..."
|
||||||
|
./configure CC="$cc" CFLAGS="$cflags" $COMMON_FLAGS $prefix
|
||||||
|
else
|
||||||
|
echo "Configuring FreeType with CFLAGS='$cflags' and PREFIX='$prefix'..."
|
||||||
|
./configure CFLAGS="$cflags" $COMMON_FLAGS $prefix
|
||||||
|
fi
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Failed to configure FreeType"
|
echo "Failed to configure FreeType"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -82,13 +89,13 @@ case "$VTYPE" in
|
|||||||
build_freetype "" "--host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32" "lib/libfreetype.a"
|
build_freetype "" "--host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32" "lib/libfreetype.a"
|
||||||
;;
|
;;
|
||||||
linux)
|
linux)
|
||||||
build_freetype "-fPIC" "" "lib/libfreetype.a"
|
build_freetype "-fPIC" "--host=x86_64-unknown-linux-gnu" "lib/libfreetype.a" "x86_64-unknown-linux-gnu-gcc"
|
||||||
;;
|
;;
|
||||||
macos)
|
macos)
|
||||||
MACOS_VERSION=10.15
|
MACOS_VERSION=10.15
|
||||||
|
|
||||||
build_freetype "-arch x86_64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-x86_64.a"
|
build_freetype "-arch x86_64 -mmacosx-version-min=$MACOS_VERSION" "--host=x86_64-apple-darwin" "tmp/libfreetype-x86_64.a"
|
||||||
build_freetype "-arch arm64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-arm64.a"
|
build_freetype "-arch arm64 -mmacosx-version-min=$MACOS_VERSION" "--host=aarch64-apple-darwin" "tmp/libfreetype-arm64.a"
|
||||||
|
|
||||||
echo "Creating universal library using lipo..."
|
echo "Creating universal library using lipo..."
|
||||||
lipo -create -output lib/libfreetype.a tmp/libfreetype-x86_64.a tmp/libfreetype-arm64.a
|
lipo -create -output lib/libfreetype.a tmp/libfreetype-x86_64.a tmp/libfreetype-arm64.a
|
||||||
|
|||||||
@@ -108,12 +108,14 @@ class GenerateLibs extends DefaultTask {
|
|||||||
|
|
||||||
if (forWindows) {
|
if (forWindows) {
|
||||||
def win64 = BuildTarget.newDefaultTarget(Os.Windows, Architecture.Bitness._64)
|
def win64 = BuildTarget.newDefaultTarget(Os.Windows, Architecture.Bitness._64)
|
||||||
|
win64.compilerPrefix = 'x86_64-w64-mingw32-'
|
||||||
addFreeTypeIfEnabled(win64)
|
addFreeTypeIfEnabled(win64)
|
||||||
buildTargets += win64
|
buildTargets += win64
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forLinux) {
|
if (forLinux) {
|
||||||
def linux64 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64)
|
def linux64 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64)
|
||||||
|
linux64.compilerPrefix = 'x86_64-unknown-linux-gnu-'
|
||||||
addFreeTypeIfEnabled(linux64)
|
addFreeTypeIfEnabled(linux64)
|
||||||
buildTargets += linux64
|
buildTargets += linux64
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.jvmargs=-Xms2G -Xmx4G
|
org.gradle.jvmargs=-Xms2G -Xmx4G
|
||||||
|
org.gradle.java.installations.paths=/Users/user/.sdkman/candidates/java/8.0.472-amzn,/Users/user/.sdkman/candidates/java/17.0.18.1-sem,/Users/user/.sdkman/candidates/java/25-amzn
|
||||||
@@ -594,9 +594,11 @@ public final class ImFontAtlas extends ImGuiStructDestroyable {
|
|||||||
/*JNI
|
/*JNI
|
||||||
#define RETURN_GLYPH_2_SHORT(glyphs) \
|
#define RETURN_GLYPH_2_SHORT(glyphs) \
|
||||||
const ImWchar* ranges = glyphs; \
|
const ImWchar* ranges = glyphs; \
|
||||||
int size = 0; \
|
int pairCount = 0; \
|
||||||
for (; ranges[0]; ranges += 2) \
|
for (; ranges[0]; ranges += 2) \
|
||||||
size += 2; \
|
pairCount++; \
|
||||||
|
int shortsPerImWchar = (int)(sizeof(ImWchar) / sizeof(jshort)); \
|
||||||
|
int size = pairCount * 2 * shortsPerImWchar + shortsPerImWchar; \
|
||||||
jshortArray jShorts = env->NewShortArray(size); \
|
jshortArray jShorts = env->NewShortArray(size); \
|
||||||
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
|
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
|
||||||
return jShorts;
|
return jShorts;
|
||||||
|
|||||||
@@ -347,6 +347,33 @@ public final class ImFontConfig extends ImGuiStructDestroyable {
|
|||||||
nSetGlyphRanges(glyphRanges);
|
nSetGlyphRanges(glyphRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience overload: encodes int[] codepoint pairs into WCHAR32 short[] layout and calls setGlyphRanges.
|
||||||
|
* Expects int[] pairs [ lo, hi, lo, hi, ..., 0 ] (zero-terminated).
|
||||||
|
* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE.
|
||||||
|
*/
|
||||||
|
public void setGlyphRanges(final int[] codepoints) {
|
||||||
|
// count pairs
|
||||||
|
int pairCount = 0;
|
||||||
|
for (int i = 0; i + 1 < codepoints.length; i += 2) {
|
||||||
|
if (codepoints[i] == 0) break;
|
||||||
|
pairCount++;
|
||||||
|
}
|
||||||
|
// each pair = 4 shorts (2 per endpoint), plus 2 terminator shorts
|
||||||
|
final short[] encoded = new short[pairCount * 4 + 2];
|
||||||
|
int out = 0;
|
||||||
|
for (int i = 0; i < pairCount; i++) {
|
||||||
|
final int lo = codepoints[i * 2];
|
||||||
|
final int hi = codepoints[i * 2 + 1];
|
||||||
|
encoded[out++] = (short) (lo & 0xFFFF);
|
||||||
|
encoded[out++] = (short) ((lo >> 16) & 0xFFFF);
|
||||||
|
encoded[out++] = (short) (hi & 0xFFFF);
|
||||||
|
encoded[out++] = (short) ((hi >> 16) & 0xFFFF);
|
||||||
|
}
|
||||||
|
// terminator already zero from array initialisation
|
||||||
|
setGlyphRanges(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
private native void nSetGlyphRanges(short[] glyphRanges); /*
|
private native void nSetGlyphRanges(short[] glyphRanges); /*
|
||||||
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
|
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import java.util.List;
|
|||||||
* You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input. For example: for a game where your script is known,
|
* You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input. For example: for a game where your script is known,
|
||||||
* if you can feed your entire script to it and only build the characters the game needs.
|
* if you can feed your entire script to it and only build the characters the game needs.
|
||||||
* <p>
|
* <p>
|
||||||
* Direct reimplementation of native ImFontGlyphRangesBuilder in java
|
* Direct reimplementation of native ImFontGlyphRangesBuilder in java.
|
||||||
|
* Built with IMGUI_USE_WCHAR32=1: supports full Unicode range U+0000..U+10FFFF.
|
||||||
|
* buildRanges() emits each range endpoint as two little-endian shorts (32-bit ImWchar layout).
|
||||||
*/
|
*/
|
||||||
public final class ImFontGlyphRangesBuilder {
|
public final class ImFontGlyphRangesBuilder {
|
||||||
private static final int UNICODE_CODEPOINT_MAX = 0xFFFF;
|
private static final int UNICODE_CODEPOINT_MAX = 0x10FFFF;
|
||||||
|
|
||||||
// have to use type long because values are unsigned integers
|
// have to use type long because values are unsigned integers; sized the same way as native (divide by 8)
|
||||||
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];
|
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];
|
||||||
|
|
||||||
public ImFontGlyphRangesBuilder() {
|
public ImFontGlyphRangesBuilder() {
|
||||||
@@ -23,56 +25,98 @@ public final class ImFontGlyphRangesBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds single character to resulting ranges
|
* Adds single character to resulting ranges (char overload for source compatibility)
|
||||||
*/
|
*/
|
||||||
public void addChar(final char c) {
|
public void addChar(final char c) {
|
||||||
setBit(c);
|
setBit(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all characters from the given string to resulting ranges
|
* Adds a Unicode codepoint (including SMP, U+10000..U+10FFFF) to resulting ranges
|
||||||
*/
|
*/
|
||||||
public void addText(final String text) {
|
public void addChar(final int codepoint) {
|
||||||
for (int i = 0; i < text.length(); i++) {
|
if (codepoint >= 0 && codepoint <= UNICODE_CODEPOINT_MAX) {
|
||||||
addChar(text.charAt(i));
|
setBit(codepoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies all given ranges to resulting ranges
|
* Adds all codepoints from the given string to resulting ranges.
|
||||||
|
* Handles supplementary characters (emoji, etc.) via codePointAt iteration.
|
||||||
|
*/
|
||||||
|
public void addText(final String text) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < text.length()) {
|
||||||
|
final int cp = text.codePointAt(i);
|
||||||
|
addChar(cp);
|
||||||
|
i += Character.charCount(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all given ranges to resulting ranges.
|
||||||
|
* Expects the WCHAR32 short[] layout: each 32-bit codepoint encoded as two little-endian shorts,
|
||||||
|
* pairs of codepoints form a [lo, hi] range, terminated by two zero shorts.
|
||||||
|
* Format: [ lo_lo16, lo_hi16, hi_lo16, hi_hi16, ..., 0, 0 ]
|
||||||
*/
|
*/
|
||||||
public void addRanges(final short[] ranges) {
|
public void addRanges(final short[] ranges) {
|
||||||
for (int i = 0; i < ranges.length; i += 2) {
|
for (int i = 0; i + 3 < ranges.length; i += 4) {
|
||||||
|
final int lo = (ranges[i] & 0xFFFF) | ((ranges[i + 1] & 0xFFFF) << 16);
|
||||||
|
final int hi = (ranges[i + 2] & 0xFFFF) | ((ranges[i + 3] & 0xFFFF) << 16);
|
||||||
|
if (lo == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int k = lo; k <= hi && k <= UNICODE_CODEPOINT_MAX; k++) {
|
||||||
|
setBit(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all given ranges to resulting ranges.
|
||||||
|
* Expects int[] pairs: [ lo, hi, lo, hi, ..., 0 ] (zero-terminated).
|
||||||
|
*/
|
||||||
|
public void addRanges(final int[] ranges) {
|
||||||
|
for (int i = 0; i + 1 < ranges.length; i += 2) {
|
||||||
if (ranges[i] == 0) {
|
if (ranges[i] == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
for (int k = ranges[i]; k <= ranges[i + 1] && k <= UNICODE_CODEPOINT_MAX; k++) {
|
||||||
for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
|
setBit(k);
|
||||||
addChar((char) k);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the final result (ordered ranges with all the unique characters submitted)
|
* Builds the final result (ordered ranges with all the unique characters submitted).
|
||||||
* Result of this function can be directly passed to ImFontAtlas
|
* Result is in WCHAR32 short[] layout: each range endpoint is two little-endian shorts,
|
||||||
|
* terminated by two zero shorts.
|
||||||
|
* Format: [ lo_lo16, lo_hi16, hi_lo16, hi_hi16, ..., 0, 0 ]
|
||||||
*/
|
*/
|
||||||
public short[] buildRanges() {
|
public short[] buildRanges() {
|
||||||
final List<Short> out = new ArrayList<>();
|
final List<Short> out = new ArrayList<>();
|
||||||
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
|
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
|
||||||
if (getBit(n)) {
|
if (getBit(n)) {
|
||||||
out.add((short) n);
|
final int lo = n;
|
||||||
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
|
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
out.add((short) n);
|
final int hi = n;
|
||||||
|
// emit lo as two LE shorts
|
||||||
|
out.add((short) (lo & 0xFFFF));
|
||||||
|
out.add((short) ((lo >> 16) & 0xFFFF));
|
||||||
|
// emit hi as two LE shorts
|
||||||
|
out.add((short) (hi & 0xFFFF));
|
||||||
|
out.add((short) ((hi >> 16) & 0xFFFF));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final short[] result = new short[out.size() + 1];
|
// terminator: two zero shorts (= one 32-bit zero ImWchar)
|
||||||
|
final short[] result = new short[out.size() + 2];
|
||||||
for (int i = 0; i < out.size(); i++) {
|
for (int i = 0; i < out.size(); i++) {
|
||||||
result[i] = out.get(i);
|
result[i] = out.get(i);
|
||||||
}
|
}
|
||||||
|
result[result.length - 2] = 0;
|
||||||
result[result.length - 1] = 0;
|
result[result.length - 1] = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,9 +256,11 @@ public final class ImFontAtlas extends ImGuiStructDestroyable {
|
|||||||
/*JNI
|
/*JNI
|
||||||
#define RETURN_GLYPH_2_SHORT(glyphs) \
|
#define RETURN_GLYPH_2_SHORT(glyphs) \
|
||||||
const ImWchar* ranges = glyphs; \
|
const ImWchar* ranges = glyphs; \
|
||||||
int size = 0; \
|
int pairCount = 0; \
|
||||||
for (; ranges[0]; ranges += 2) \
|
for (; ranges[0]; ranges += 2) \
|
||||||
size += 2; \
|
pairCount++; \
|
||||||
|
int shortsPerImWchar = (int)(sizeof(ImWchar) / sizeof(jshort)); \
|
||||||
|
int size = pairCount * 2 * shortsPerImWchar + shortsPerImWchar; \
|
||||||
jshortArray jShorts = env->NewShortArray(size); \
|
jshortArray jShorts = env->NewShortArray(size); \
|
||||||
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
|
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
|
||||||
return jShorts;
|
return jShorts;
|
||||||
|
|||||||
@@ -124,6 +124,33 @@ public final class ImFontConfig extends ImGuiStructDestroyable {
|
|||||||
nSetGlyphRanges(glyphRanges);
|
nSetGlyphRanges(glyphRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience overload: encodes int[] codepoint pairs into WCHAR32 short[] layout and calls setGlyphRanges.
|
||||||
|
* Expects int[] pairs [ lo, hi, lo, hi, ..., 0 ] (zero-terminated).
|
||||||
|
* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE.
|
||||||
|
*/
|
||||||
|
public void setGlyphRanges(final int[] codepoints) {
|
||||||
|
// count pairs
|
||||||
|
int pairCount = 0;
|
||||||
|
for (int i = 0; i + 1 < codepoints.length; i += 2) {
|
||||||
|
if (codepoints[i] == 0) break;
|
||||||
|
pairCount++;
|
||||||
|
}
|
||||||
|
// each pair = 4 shorts (2 per endpoint), plus 2 terminator shorts
|
||||||
|
final short[] encoded = new short[pairCount * 4 + 2];
|
||||||
|
int out = 0;
|
||||||
|
for (int i = 0; i < pairCount; i++) {
|
||||||
|
final int lo = codepoints[i * 2];
|
||||||
|
final int hi = codepoints[i * 2 + 1];
|
||||||
|
encoded[out++] = (short) (lo & 0xFFFF);
|
||||||
|
encoded[out++] = (short) ((lo >> 16) & 0xFFFF);
|
||||||
|
encoded[out++] = (short) (hi & 0xFFFF);
|
||||||
|
encoded[out++] = (short) ((hi >> 16) & 0xFFFF);
|
||||||
|
}
|
||||||
|
// terminator already zero from array initialisation
|
||||||
|
setGlyphRanges(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
private native void nSetGlyphRanges(short[] glyphRanges); /*
|
private native void nSetGlyphRanges(short[] glyphRanges); /*
|
||||||
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
|
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import java.util.List;
|
|||||||
* You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input. For example: for a game where your script is known,
|
* You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input. For example: for a game where your script is known,
|
||||||
* if you can feed your entire script to it and only build the characters the game needs.
|
* if you can feed your entire script to it and only build the characters the game needs.
|
||||||
* <p>
|
* <p>
|
||||||
* Direct reimplementation of native ImFontGlyphRangesBuilder in java
|
* Direct reimplementation of native ImFontGlyphRangesBuilder in java.
|
||||||
|
* Built with IMGUI_USE_WCHAR32=1: supports full Unicode range U+0000..U+10FFFF.
|
||||||
|
* buildRanges() emits each range endpoint as two little-endian shorts (32-bit ImWchar layout).
|
||||||
*/
|
*/
|
||||||
public final class ImFontGlyphRangesBuilder {
|
public final class ImFontGlyphRangesBuilder {
|
||||||
private static final int UNICODE_CODEPOINT_MAX = 0xFFFF;
|
private static final int UNICODE_CODEPOINT_MAX = 0x10FFFF;
|
||||||
|
|
||||||
// have to use type long because values are unsigned integers
|
// have to use type long because values are unsigned integers; sized the same way as native (divide by 8)
|
||||||
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];
|
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];
|
||||||
|
|
||||||
public ImFontGlyphRangesBuilder() {
|
public ImFontGlyphRangesBuilder() {
|
||||||
@@ -23,56 +25,98 @@ public final class ImFontGlyphRangesBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds single character to resulting ranges
|
* Adds single character to resulting ranges (char overload for source compatibility)
|
||||||
*/
|
*/
|
||||||
public void addChar(final char c) {
|
public void addChar(final char c) {
|
||||||
setBit(c);
|
setBit(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all characters from the given string to resulting ranges
|
* Adds a Unicode codepoint (including SMP, U+10000..U+10FFFF) to resulting ranges
|
||||||
*/
|
*/
|
||||||
public void addText(final String text) {
|
public void addChar(final int codepoint) {
|
||||||
for (int i = 0; i < text.length(); i++) {
|
if (codepoint >= 0 && codepoint <= UNICODE_CODEPOINT_MAX) {
|
||||||
addChar(text.charAt(i));
|
setBit(codepoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies all given ranges to resulting ranges
|
* Adds all codepoints from the given string to resulting ranges.
|
||||||
|
* Handles supplementary characters (emoji, etc.) via codePointAt iteration.
|
||||||
|
*/
|
||||||
|
public void addText(final String text) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < text.length()) {
|
||||||
|
final int cp = text.codePointAt(i);
|
||||||
|
addChar(cp);
|
||||||
|
i += Character.charCount(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all given ranges to resulting ranges.
|
||||||
|
* Expects the WCHAR32 short[] layout: each 32-bit codepoint encoded as two little-endian shorts,
|
||||||
|
* pairs of codepoints form a [lo, hi] range, terminated by two zero shorts.
|
||||||
|
* Format: [ lo_lo16, lo_hi16, hi_lo16, hi_hi16, ..., 0, 0 ]
|
||||||
*/
|
*/
|
||||||
public void addRanges(final short[] ranges) {
|
public void addRanges(final short[] ranges) {
|
||||||
for (int i = 0; i < ranges.length; i += 2) {
|
for (int i = 0; i + 3 < ranges.length; i += 4) {
|
||||||
|
final int lo = (ranges[i] & 0xFFFF) | ((ranges[i + 1] & 0xFFFF) << 16);
|
||||||
|
final int hi = (ranges[i + 2] & 0xFFFF) | ((ranges[i + 3] & 0xFFFF) << 16);
|
||||||
|
if (lo == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int k = lo; k <= hi && k <= UNICODE_CODEPOINT_MAX; k++) {
|
||||||
|
setBit(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all given ranges to resulting ranges.
|
||||||
|
* Expects int[] pairs: [ lo, hi, lo, hi, ..., 0 ] (zero-terminated).
|
||||||
|
*/
|
||||||
|
public void addRanges(final int[] ranges) {
|
||||||
|
for (int i = 0; i + 1 < ranges.length; i += 2) {
|
||||||
if (ranges[i] == 0) {
|
if (ranges[i] == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
for (int k = ranges[i]; k <= ranges[i + 1] && k <= UNICODE_CODEPOINT_MAX; k++) {
|
||||||
for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
|
setBit(k);
|
||||||
addChar((char) k);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the final result (ordered ranges with all the unique characters submitted)
|
* Builds the final result (ordered ranges with all the unique characters submitted).
|
||||||
* Result of this function can be directly passed to ImFontAtlas
|
* Result is in WCHAR32 short[] layout: each range endpoint is two little-endian shorts,
|
||||||
|
* terminated by two zero shorts.
|
||||||
|
* Format: [ lo_lo16, lo_hi16, hi_lo16, hi_hi16, ..., 0, 0 ]
|
||||||
*/
|
*/
|
||||||
public short[] buildRanges() {
|
public short[] buildRanges() {
|
||||||
final List<Short> out = new ArrayList<>();
|
final List<Short> out = new ArrayList<>();
|
||||||
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
|
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
|
||||||
if (getBit(n)) {
|
if (getBit(n)) {
|
||||||
out.add((short) n);
|
final int lo = n;
|
||||||
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
|
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
out.add((short) n);
|
final int hi = n;
|
||||||
|
// emit lo as two LE shorts
|
||||||
|
out.add((short) (lo & 0xFFFF));
|
||||||
|
out.add((short) ((lo >> 16) & 0xFFFF));
|
||||||
|
// emit hi as two LE shorts
|
||||||
|
out.add((short) (hi & 0xFFFF));
|
||||||
|
out.add((short) ((hi >> 16) & 0xFFFF));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final short[] result = new short[out.size() + 1];
|
// terminator: two zero shorts (= one 32-bit zero ImWchar)
|
||||||
|
final short[] result = new short[out.size() + 2];
|
||||||
for (int i = 0; i < out.size(); i++) {
|
for (int i = 0; i < out.size(); i++) {
|
||||||
result[i] = out.get(i);
|
result[i] = out.get(i);
|
||||||
}
|
}
|
||||||
|
result[result.length - 2] = 0;
|
||||||
result[result.length - 1] = 0;
|
result[result.length - 1] = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
//#define IMGUI_USE_WCHAR32
|
#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||||
|
|||||||
+5
-7
@@ -12,19 +12,17 @@ ext.configurePublishing = { packageName, packageDesc, packageVersion ->
|
|||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = 'MavenCentral'
|
name = 'MrLokop'
|
||||||
def releasesRepoUrl = 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2'
|
url = 'https://mvn.mrlokop.dev/releases'
|
||||||
def snapshotsRepoUrl = 'https://central.sonatype.com/repository/maven-snapshots'
|
|
||||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
|
||||||
credentials {
|
credentials {
|
||||||
username = System.getenv('NEXUS_UPD_ID')?.trim() ?: ''
|
username = 'AutoRobot'
|
||||||
password = System.getenv('NEXUS_UPD_PASS')?.trim() ?: ''
|
password = '8Mr3OAv4WcRNHZS3lAVDx32sWt71YS7GsBvN7TZhCGMmDeSz'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
publications {
|
publications {
|
||||||
imgui(MavenPublication) {
|
imgui(MavenPublication) {
|
||||||
groupId = 'io.github.spair'
|
groupId = 'org.nnstd.imgui'
|
||||||
artifactId = packageName
|
artifactId = packageName
|
||||||
version = packageVersion
|
version = packageVersion
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version("0.10.0")
|
id("org.gradle.toolchains.foojay-resolver-convention") version("0.9.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "imgui-java"
|
rootProject.name = "imgui-java"
|
||||||
|
|||||||
Reference in new issue
Block a user