diff --git a/BUILDING.md b/BUILDING.md
new file mode 100644
index 0000000..14dc273
--- /dev/null
+++ b/BUILDING.md
@@ -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
+
+ mrlokop
+ https://mvn.mrlokop.dev/releases
+
+
+
+ org.nnstd.imgui
+ imgui-java-binding
+ 1.90.0-wchar32+mrlokop2
+
+
+ org.nnstd.imgui
+ imgui-java-lwjgl3
+ 1.90.0-wchar32+mrlokop2
+
+
+
+ org.nnstd.imgui
+ imgui-java-natives-macos
+ 1.90.0-wchar32+mrlokop2
+
+
+ org.nnstd.imgui
+ imgui-java-natives-linux
+ 1.90.0-wchar32+mrlokop2
+
+
+ org.nnstd.imgui
+ imgui-java-natives-windows
+ 1.90.0-wchar32+mrlokop2
+
+
+
+ org.nnstd.imgui
+ imgui-java-app
+ 1.90.0-wchar32+mrlokop2
+
+```
+
+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);
+```
diff --git a/bin/imgui-java64.dll b/bin/imgui-java64.dll
index a2d8549..9118d81 100644
Binary files a/bin/imgui-java64.dll and b/bin/imgui-java64.dll differ
diff --git a/bin/libimgui-java64.dylib b/bin/libimgui-java64.dylib
index d9796b2..935b7fa 100644
Binary files a/bin/libimgui-java64.dylib and b/bin/libimgui-java64.dylib differ
diff --git a/bin/libimgui-java64.so b/bin/libimgui-java64.so
index 067c4e5..9793da9 100644
Binary files a/bin/libimgui-java64.so and b/bin/libimgui-java64.so differ
diff --git a/build.gradle b/build.gradle
index 9cdceb3..1f294d3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ ext {
allprojects {
group = 'imgui-java'
- version = 'git describe --tags --always'.execute().text.trim().substring(1)
+ version = '1.90.0-wchar32+mrlokop2'
repositories {
mavenCentral()
diff --git a/buildSrc/scripts/vendor_freetype.sh b/buildSrc/scripts/vendor_freetype.sh
index 5d973c7..f1c47b6 100755
--- a/buildSrc/scripts/vendor_freetype.sh
+++ b/buildSrc/scripts/vendor_freetype.sh
@@ -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"
# Function to configure and build FreeType
+# Args: cflags prefix output_dir [cc]
build_freetype() {
cflags=$1
prefix=$2
output_dir=$3
+ cc=${4:-}
echo "Cleaning previous builds..."
make clean
- echo "Configuring FreeType with CFLAGS='$cflags' and PREFIX='$prefix'..."
- ./configure CFLAGS="$cflags" $COMMON_FLAGS $prefix
+ if [ -n "$cc" ]; then
+ 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
echo "Failed to configure FreeType"
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"
;;
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_VERSION=10.15
- build_freetype "-arch x86_64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-x86_64.a"
- build_freetype "-arch arm64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-arm64.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" "--host=aarch64-apple-darwin" "tmp/libfreetype-arm64.a"
echo "Creating universal library using lipo..."
lipo -create -output lib/libfreetype.a tmp/libfreetype-x86_64.a tmp/libfreetype-arm64.a
diff --git a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy
index 3a0c000..5410c27 100644
--- a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy
+++ b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy
@@ -108,12 +108,14 @@ class GenerateLibs extends DefaultTask {
if (forWindows) {
def win64 = BuildTarget.newDefaultTarget(Os.Windows, Architecture.Bitness._64)
+ win64.compilerPrefix = 'x86_64-w64-mingw32-'
addFreeTypeIfEnabled(win64)
buildTargets += win64
}
if (forLinux) {
def linux64 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64)
+ linux64.compilerPrefix = 'x86_64-unknown-linux-gnu-'
addFreeTypeIfEnabled(linux64)
buildTargets += linux64
}
diff --git a/gradle.properties b/gradle.properties
index ee149e8..16c38a8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,4 @@
org.gradle.parallel=true
org.gradle.caching=true
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
diff --git a/imgui-binding/src/generated/java/imgui/ImFontAtlas.java b/imgui-binding/src/generated/java/imgui/ImFontAtlas.java
index fed5a9f..c242d38 100644
--- a/imgui-binding/src/generated/java/imgui/ImFontAtlas.java
+++ b/imgui-binding/src/generated/java/imgui/ImFontAtlas.java
@@ -594,9 +594,11 @@ public final class ImFontAtlas extends ImGuiStructDestroyable {
/*JNI
#define RETURN_GLYPH_2_SHORT(glyphs) \
const ImWchar* ranges = glyphs; \
- int size = 0; \
+ int pairCount = 0; \
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); \
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
return jShorts;
diff --git a/imgui-binding/src/generated/java/imgui/ImFontConfig.java b/imgui-binding/src/generated/java/imgui/ImFontConfig.java
index 4ee7811..138e0ea 100644
--- a/imgui-binding/src/generated/java/imgui/ImFontConfig.java
+++ b/imgui-binding/src/generated/java/imgui/ImFontConfig.java
@@ -347,6 +347,33 @@ public final class ImFontConfig extends ImGuiStructDestroyable {
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); /*
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
*/
diff --git a/imgui-binding/src/generated/java/imgui/ImFontGlyphRangesBuilder.java b/imgui-binding/src/generated/java/imgui/ImFontGlyphRangesBuilder.java
index b9f85ba..3384830 100644
--- a/imgui-binding/src/generated/java/imgui/ImFontGlyphRangesBuilder.java
+++ b/imgui-binding/src/generated/java/imgui/ImFontGlyphRangesBuilder.java
@@ -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,
* if you can feed your entire script to it and only build the characters the game needs.
*
- * 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 {
- 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];
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) {
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) {
- for (int i = 0; i < text.length(); i++) {
- addChar(text.charAt(i));
+ public void addChar(final int codepoint) {
+ if (codepoint >= 0 && codepoint <= UNICODE_CODEPOINT_MAX) {
+ 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) {
- 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) {
break;
}
-
- for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
- addChar((char) k);
+ for (int k = ranges[i]; k <= ranges[i + 1] && k <= UNICODE_CODEPOINT_MAX; k++) {
+ setBit(k);
}
}
}
/**
- * Builds the final result (ordered ranges with all the unique characters submitted)
- * Result of this function can be directly passed to ImFontAtlas
+ * Builds the final result (ordered ranges with all the unique characters submitted).
+ * 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() {
final List out = new ArrayList<>();
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
if (getBit(n)) {
- out.add((short) n);
+ final int lo = n;
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
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++) {
result[i] = out.get(i);
}
+ result[result.length - 2] = 0;
result[result.length - 1] = 0;
return result;
}
diff --git a/imgui-binding/src/main/java/imgui/ImFontAtlas.java b/imgui-binding/src/main/java/imgui/ImFontAtlas.java
index bab43a6..8e0f2be 100644
--- a/imgui-binding/src/main/java/imgui/ImFontAtlas.java
+++ b/imgui-binding/src/main/java/imgui/ImFontAtlas.java
@@ -256,9 +256,11 @@ public final class ImFontAtlas extends ImGuiStructDestroyable {
/*JNI
#define RETURN_GLYPH_2_SHORT(glyphs) \
const ImWchar* ranges = glyphs; \
- int size = 0; \
+ int pairCount = 0; \
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); \
env->SetShortArrayRegion(jShorts, 0, size, (jshort*)glyphs); \
return jShorts;
diff --git a/imgui-binding/src/main/java/imgui/ImFontConfig.java b/imgui-binding/src/main/java/imgui/ImFontConfig.java
index c366149..107bcf9 100644
--- a/imgui-binding/src/main/java/imgui/ImFontConfig.java
+++ b/imgui-binding/src/main/java/imgui/ImFontConfig.java
@@ -124,6 +124,33 @@ public final class ImFontConfig extends ImGuiStructDestroyable {
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); /*
THIS->GlyphRanges = glyphRanges != NULL ? (ImWchar*)&glyphRanges[0] : NULL;
*/
diff --git a/imgui-binding/src/main/java/imgui/ImFontGlyphRangesBuilder.java b/imgui-binding/src/main/java/imgui/ImFontGlyphRangesBuilder.java
index b9f85ba..3384830 100644
--- a/imgui-binding/src/main/java/imgui/ImFontGlyphRangesBuilder.java
+++ b/imgui-binding/src/main/java/imgui/ImFontGlyphRangesBuilder.java
@@ -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,
* if you can feed your entire script to it and only build the characters the game needs.
*
- * 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 {
- 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];
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) {
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) {
- for (int i = 0; i < text.length(); i++) {
- addChar(text.charAt(i));
+ public void addChar(final int codepoint) {
+ if (codepoint >= 0 && codepoint <= UNICODE_CODEPOINT_MAX) {
+ 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) {
- 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) {
break;
}
-
- for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
- addChar((char) k);
+ for (int k = ranges[i]; k <= ranges[i + 1] && k <= UNICODE_CODEPOINT_MAX; k++) {
+ setBit(k);
}
}
}
/**
- * Builds the final result (ordered ranges with all the unique characters submitted)
- * Result of this function can be directly passed to ImFontAtlas
+ * Builds the final result (ordered ranges with all the unique characters submitted).
+ * 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() {
final List out = new ArrayList<>();
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
if (getBit(n)) {
- out.add((short) n);
+ final int lo = n;
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
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++) {
result[i] = out.get(i);
}
+ result[result.length - 2] = 0;
result[result.length - 1] = 0;
return result;
}
diff --git a/imgui-binding/src/main/native/imconfig.h b/imgui-binding/src/main/native/imconfig.h
index 917f031..52d9912 100644
--- a/imgui-binding/src/main/native/imconfig.h
+++ b/imgui-binding/src/main/native/imconfig.h
@@ -59,7 +59,7 @@
//#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...)
-//#define IMGUI_USE_WCHAR32
+#define IMGUI_USE_WCHAR32
//---- 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.
diff --git a/publish.gradle b/publish.gradle
index 270918c..996489e 100644
--- a/publish.gradle
+++ b/publish.gradle
@@ -12,19 +12,17 @@ ext.configurePublishing = { packageName, packageDesc, packageVersion ->
publishing {
repositories {
maven {
- name = 'MavenCentral'
- def releasesRepoUrl = 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2'
- def snapshotsRepoUrl = 'https://central.sonatype.com/repository/maven-snapshots'
- url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
+ name = 'MrLokop'
+ url = 'https://mvn.mrlokop.dev/releases'
credentials {
- username = System.getenv('NEXUS_UPD_ID')?.trim() ?: ''
- password = System.getenv('NEXUS_UPD_PASS')?.trim() ?: ''
+ username = 'AutoRobot'
+ password = '8Mr3OAv4WcRNHZS3lAVDx32sWt71YS7GsBvN7TZhCGMmDeSz'
}
}
}
publications {
imgui(MavenPublication) {
- groupId = 'io.github.spair'
+ groupId = 'org.nnstd.imgui'
artifactId = packageName
version = packageVersion
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2029f83..96c2c94 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,5 +1,5 @@
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"