Skip to content

Commit 36e59c4

Browse files
authored
[API] ImFontGlyphRangesBuilder and fonts example
1 parent 87fa05d commit 36e59c4

File tree

7 files changed

+1157
-4
lines changed

7 files changed

+1157
-4
lines changed

example/src/main/java/FontAwesomeIcons.java

Lines changed: 1015 additions & 0 deletions
Large diffs are not rendered by default.

example/src/main/java/Main.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import imgui.ImGui;
2-
import imgui.ImGuiIO;
1+
import imgui.*;
32
import imgui.app.Application;
43
import imgui.app.Configuration;
54
import imgui.extension.imnodes.ImNodes;
65
import imgui.flag.ImGuiConfigFlags;
76
import imgui.flag.ImGuiInputTextFlags;
87
import imgui.type.ImString;
98

9+
import java.io.IOException;
10+
import java.net.URISyntaxException;
11+
import java.nio.file.Files;
12+
import java.nio.file.Paths;
13+
1014
public class Main extends Application {
1115
private final ImString str = new ImString(5);
1216
private final float[] flt = new float[1];
@@ -29,6 +33,34 @@ protected void initImGui(final Configuration config) {
2933
io.setConfigViewportsNoTaskBarIcon(true);
3034

3135
ImNodes.createContext();
36+
37+
// Example of fonts configuration
38+
// For more information read: https://github.com/ocornut/imgui/blob/33cdbe97b8fd233c6c12ca216e76398c2e89b0d8/docs/FONTS.md
39+
40+
io.getFonts().addFontDefault(); // Add default font for latin glyphs
41+
42+
// You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input.
43+
// For example: for a game where your script is known, if you can feed your entire script to it (using addText) and only build the characters the game needs.
44+
// Here we are using it just to combine all required glyphs in one place
45+
final ImFontGlyphRangesBuilder rangesBuilder = new ImFontGlyphRangesBuilder(); // Glyphs ranges provide
46+
rangesBuilder.addRanges(io.getFonts().getGlyphRangesDefault());
47+
rangesBuilder.addRanges(io.getFonts().getGlyphRangesCyrillic());
48+
rangesBuilder.addRanges(io.getFonts().getGlyphRangesJapanese());
49+
rangesBuilder.addRanges(FontAwesomeIcons._IconRange);
50+
51+
// Font config for additional fonts
52+
// This is a natively allocated struct so don't forget to call destroy after atlas is built
53+
final ImFontConfig fontConfig = new ImFontConfig();
54+
fontConfig.setMergeMode(true); // Enable merge mode to merge cyrillic, japanese and icons with default font
55+
56+
final short[] glyphRanges = rangesBuilder.buildRanges();
57+
io.getFonts().addFontFromMemoryTTF(loadFromResources("Tahoma.ttf"), 14, fontConfig, glyphRanges); // cyrillic glyphs
58+
io.getFonts().addFontFromMemoryTTF(loadFromResources("NotoSansCJKjp-Medium.otf"), 14, fontConfig, glyphRanges); // japanese glyphs
59+
io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-regular-400.ttf"), 14, fontConfig, glyphRanges); // font awesome
60+
io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-solid-900.ttf"), 14, fontConfig, glyphRanges); // font awesome
61+
io.getFonts().build();
62+
63+
fontConfig.destroy();
3264
}
3365

3466
@Override
@@ -39,8 +71,8 @@ protected void disposeImGui() {
3971

4072
@Override
4173
public void process() {
42-
ImGui.text("Hello, World!");
43-
if (ImGui.button("Save")) {
74+
ImGui.text("Hello, World! " + FontAwesomeIcons.Smile);
75+
if (ImGui.button(FontAwesomeIcons.Save + " Save")) {
4476
count++;
4577
}
4678
ImGui.sameLine();
@@ -53,6 +85,14 @@ public void process() {
5385
Extra.show(this);
5486
}
5587

88+
private static byte[] loadFromResources(String name) {
89+
try {
90+
return Files.readAllBytes(Paths.get(Main.class.getResource(name).toURI()));
91+
} catch (IOException | URISyntaxException e) {
92+
throw new RuntimeException(e);
93+
}
94+
}
95+
5696
public static void main(final String[] args) {
5797
launch(new Main());
5898
}
15.7 MB
Binary file not shown.
303 KB
Binary file not shown.
33.3 KB
Binary file not shown.
200 KB
Binary file not shown.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package imgui;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
7+
/**
8+
* Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call buildRanges().
9+
* <p>
10+
* You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input. For example: for a game where your script is known,
11+
* if you can feed your entire script to it and only build the characters the game needs.
12+
* <p>
13+
* Direct reimplementation of native ImFontGlyphRangesBuilder in java
14+
*/
15+
public final class ImFontGlyphRangesBuilder {
16+
17+
private static final int UNICODE_CODEPOINT_MAX = 0xFFFF;
18+
19+
// have to use type long because values are unsigned integers
20+
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];
21+
22+
public ImFontGlyphRangesBuilder() {
23+
clear();
24+
}
25+
26+
/**
27+
* Adds single character to resulting ranges
28+
*/
29+
public void addChar(final char c) {
30+
setBit(c);
31+
}
32+
33+
/**
34+
* Adds all characters from the given string to resulting ranges
35+
*/
36+
public void addText(final String text) {
37+
for (int i = 0; i < text.length(); i++) {
38+
addChar(text.charAt(i));
39+
}
40+
}
41+
42+
/**
43+
* Copies all given ranges to resulting ranges
44+
*/
45+
public void addRanges(final short[] ranges) {
46+
for (int i = 0; i < ranges.length; i += 2) {
47+
if (ranges[i] == 0) {
48+
break;
49+
}
50+
51+
for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
52+
addChar((char) k);
53+
}
54+
}
55+
}
56+
57+
/**
58+
* Builds the final result (ordered ranges with all the unique characters submitted)
59+
* Result of this function can be directly passed to ImFontAtlas
60+
*/
61+
public short[] buildRanges() {
62+
final List<Short> out = new ArrayList<>();
63+
for (int n = 0; n <= UNICODE_CODEPOINT_MAX; n++) {
64+
if (getBit(n)) {
65+
out.add((short) n);
66+
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
67+
n++;
68+
}
69+
out.add((short) n);
70+
}
71+
}
72+
73+
final short[] result = new short[out.size() + 1];
74+
for (int i = 0; i < out.size(); i++) {
75+
result[i] = out.get(i);
76+
}
77+
result[result.length - 1] = 0;
78+
return result;
79+
}
80+
81+
public void clear() {
82+
Arrays.fill(usedChars, 0L);
83+
}
84+
85+
public void setBit(final int n) {
86+
final int off = n >> 5;
87+
final long mask = 1L << (n & 31L);
88+
usedChars[off] |= mask;
89+
}
90+
91+
public boolean getBit(final int n) {
92+
final int off = n >> 5;
93+
final long mask = 1L << (n & 31L);
94+
return (usedChars[off] & mask) > 0;
95+
}
96+
97+
98+
}

0 commit comments

Comments
 (0)