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

- 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:
RobotandClaude Sonnet 4.6 committed 2026-04-20 10:29:43 +03:00
1 parent 2fcd517b3e
commit 34c40304f6
17 files changed
+439 -55

No files matched your search

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