Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API] ImFontGlyphRangesBuilder and fonts example #51

Merged
merged 1 commit into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}


}