Skip to content

Commit

Permalink
[API] ImFontGlyphRangesBuilder and fonts example
Browse files Browse the repository at this point in the history
  • Loading branch information
abvadabra committed Apr 29, 2021
1 parent 87fa05d commit 36e59c4
Show file tree
Hide file tree
Showing 7 changed files with 1,157 additions and 4 deletions.
1,015 changes: 1,015 additions & 0 deletions example/src/main/java/FontAwesomeIcons.java

Large diffs are not rendered by default.

48 changes: 44 additions & 4 deletions example/src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import imgui.ImGui;
import imgui.ImGuiIO;
import imgui.*;
import imgui.app.Application;
import imgui.app.Configuration;
import imgui.extension.imnodes.ImNodes;
import imgui.flag.ImGuiConfigFlags;
import imgui.flag.ImGuiInputTextFlags;
import imgui.type.ImString;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main extends Application {
private final ImString str = new ImString(5);
private final float[] flt = new float[1];
Expand All @@ -29,6 +33,34 @@ protected void initImGui(final Configuration config) {
io.setConfigViewportsNoTaskBarIcon(true);

ImNodes.createContext();

// Example of fonts configuration
// For more information read: https://github.com/ocornut/imgui/blob/33cdbe97b8fd233c6c12ca216e76398c2e89b0d8/docs/FONTS.md

io.getFonts().addFontDefault(); // Add default font for latin glyphs

// 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 (using addText) and only build the characters the game needs.
// Here we are using it just to combine all required glyphs in one place
final ImFontGlyphRangesBuilder rangesBuilder = new ImFontGlyphRangesBuilder(); // Glyphs ranges provide
rangesBuilder.addRanges(io.getFonts().getGlyphRangesDefault());
rangesBuilder.addRanges(io.getFonts().getGlyphRangesCyrillic());
rangesBuilder.addRanges(io.getFonts().getGlyphRangesJapanese());
rangesBuilder.addRanges(FontAwesomeIcons._IconRange);

// Font config for additional fonts
// This is a natively allocated struct so don't forget to call destroy after atlas is built
final ImFontConfig fontConfig = new ImFontConfig();
fontConfig.setMergeMode(true); // Enable merge mode to merge cyrillic, japanese and icons with default font

final short[] glyphRanges = rangesBuilder.buildRanges();
io.getFonts().addFontFromMemoryTTF(loadFromResources("Tahoma.ttf"), 14, fontConfig, glyphRanges); // cyrillic glyphs
io.getFonts().addFontFromMemoryTTF(loadFromResources("NotoSansCJKjp-Medium.otf"), 14, fontConfig, glyphRanges); // japanese glyphs
io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-regular-400.ttf"), 14, fontConfig, glyphRanges); // font awesome
io.getFonts().addFontFromMemoryTTF(loadFromResources("fa-solid-900.ttf"), 14, fontConfig, glyphRanges); // font awesome
io.getFonts().build();

fontConfig.destroy();
}

@Override
Expand All @@ -39,8 +71,8 @@ protected void disposeImGui() {

@Override
public void process() {
ImGui.text("Hello, World!");
if (ImGui.button("Save")) {
ImGui.text("Hello, World! " + FontAwesomeIcons.Smile);
if (ImGui.button(FontAwesomeIcons.Save + " Save")) {
count++;
}
ImGui.sameLine();
Expand All @@ -53,6 +85,14 @@ public void process() {
Extra.show(this);
}

private static byte[] loadFromResources(String name) {
try {
return Files.readAllBytes(Paths.get(Main.class.getResource(name).toURI()));
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
}

public static void main(final String[] args) {
launch(new Main());
}
Expand Down
Binary file not shown.
Binary file added example/src/main/resources/Tahoma.ttf
Binary file not shown.
Binary file added example/src/main/resources/fa-regular-400.ttf
Binary file not shown.
Binary file added example/src/main/resources/fa-solid-900.ttf
Binary file not shown.
98 changes: 98 additions & 0 deletions imgui-binding/src/main/java/imgui/ImFontGlyphRangesBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package imgui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call buildRanges().
* <p>
* 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
*/
public final class ImFontGlyphRangesBuilder {

private static final int UNICODE_CODEPOINT_MAX = 0xFFFF;

// have to use type long because values are unsigned integers
private final long[] usedChars = new long[(UNICODE_CODEPOINT_MAX + 1) / 8];

public ImFontGlyphRangesBuilder() {
clear();
}

/**
* Adds single character to resulting ranges
*/
public void addChar(final char c) {
setBit(c);
}

/**
* Adds all characters from the given string to resulting ranges
*/
public void addText(final String text) {
for (int i = 0; i < text.length(); i++) {
addChar(text.charAt(i));
}
}

/**
* Copies all given ranges to resulting ranges
*/
public void addRanges(final short[] ranges) {
for (int i = 0; i < ranges.length; i += 2) {
if (ranges[i] == 0) {
break;
}

for (int k = ranges[i]; k <= ranges[i + 1]; k++) {
addChar((char) k);
}
}
}

/**
* Builds the final result (ordered ranges with all the unique characters submitted)
* Result of this function can be directly passed to ImFontAtlas
*/
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);
while (n < UNICODE_CODEPOINT_MAX && getBit(n + 1)) {
n++;
}
out.add((short) n);
}
}

final short[] result = new short[out.size() + 1];
for (int i = 0; i < out.size(); i++) {
result[i] = out.get(i);
}
result[result.length - 1] = 0;
return result;
}

public void clear() {
Arrays.fill(usedChars, 0L);
}

public void setBit(final int n) {
final int off = n >> 5;
final long mask = 1L << (n & 31L);
usedChars[off] |= mask;
}

public boolean getBit(final int n) {
final int off = n >> 5;
final long mask = 1L << (n & 31L);
return (usedChars[off] & mask) > 0;
}


}

0 comments on commit 36e59c4

Please sign in to comment.