feat: unicode emoji support + other stuff
This commit is contained in:
parent
6ac139321b
commit
d0f8aec244
BIN
assets/opensb/font/twemoji.woff2
Normal file
BIN
assets/opensb/font/twemoji.woff2
Normal file
Binary file not shown.
@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
|
"font" : {
|
||||||
|
"defaultFont" : "hobo",
|
||||||
|
"fallbackFont" : "unifont",
|
||||||
|
"emojiFont" : "twemoji"
|
||||||
|
},
|
||||||
"nametag" : {
|
"nametag" : {
|
||||||
"showMasterNames" : true,
|
"showMasterNames" : true,
|
||||||
// border is double here as it otherwise fades out near the end
|
// border is double here as it otherwise fades out near the end
|
||||||
// (could just the end to opaque too, but then diagonals are more jaggy)
|
// (could just the end to opaque too, but then diagonals are more jaggy)
|
||||||
"textStyle" : { "backDirectives" : "?border=1;222;2224?border=1;222;2224" },
|
"textStyle" : { "backDirectives" : "border=1;222;2224?border=1;222;2224" },
|
||||||
"inspectOpacityRate" : 0.15,
|
"inspectOpacityRate" : 0.15,
|
||||||
"movementThreshold" : 0.5,
|
"movementThreshold" : 0.5,
|
||||||
"offset" : [0, 13]
|
"offset" : [0, 13]
|
||||||
@ -19,7 +24,7 @@
|
|||||||
"paneTextStyle" : {},
|
"paneTextStyle" : {},
|
||||||
"textBoxTextStyle" : {},
|
"textBoxTextStyle" : {},
|
||||||
"itemSlotTextStyle" : {
|
"itemSlotTextStyle" : {
|
||||||
"backDirectives" : "?border=1;444;4444"
|
"backDirectives" : "border=1;444;4444"
|
||||||
},
|
},
|
||||||
"teamBarNameStyle" : {},
|
"teamBarNameStyle" : {},
|
||||||
"cursorTooltip" : { "textStyle" : {} },
|
"cursorTooltip" : { "textStyle" : {} },
|
||||||
@ -27,7 +32,7 @@
|
|||||||
"debugTextStyle" : {
|
"debugTextStyle" : {
|
||||||
"font" : "iosevka-semibold",
|
"font" : "iosevka-semibold",
|
||||||
"fontSize" : 7,
|
"fontSize" : 7,
|
||||||
"backDirectives" : "?border=2;111a;1114"
|
"backDirectives" : "border=1;333a;3334"
|
||||||
},
|
},
|
||||||
"debugSpatialClearTime" : 0.0,
|
"debugSpatialClearTime" : 0.0,
|
||||||
"debugOffset" : [80, 130],
|
"debugOffset" : [80, 130],
|
||||||
@ -35,7 +40,7 @@
|
|||||||
// Change planet name to support the new internal string formatting.
|
// Change planet name to support the new internal string formatting.
|
||||||
"planetNameFormatString" : "- {} -",
|
"planetNameFormatString" : "- {} -",
|
||||||
"planetTextStyle" : {
|
"planetTextStyle" : {
|
||||||
"backDirectives" : "?border=6;000;000",
|
"backDirectives" : "border=6;000;000",
|
||||||
"fontSize" : 24
|
"fontSize" : 24
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"config" : {
|
"config" : {
|
||||||
"lineHeight" : 1,
|
"lineHeight" : 1,
|
||||||
"padding" : [1, 1],
|
"padding" : [1, 1],
|
||||||
"textStyle" : { "backDirectives" : "?border=1;111a;1114" },
|
"textStyle" : { "backDirectives" : "border=1;111a;1117" },
|
||||||
|
"chatFormatString" : "^backdirectives=border=1=333=3337;<^backdirectives=border=1=333=3330?border=1=333=3337,white;{}^reset,backdirectives=border=1=333=3337;>^reset,#eee; {}",
|
||||||
"colors" : {
|
"colors" : {
|
||||||
"local" : "^white;",
|
"local" : "^white;",
|
||||||
"party" : "^blue;",
|
"party" : "^blue;",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
constexpr int FontLoadFlags = FT_LOAD_FORCE_AUTOHINT;
|
constexpr int FontLoadFlags = FT_LOAD_NO_SVG | FT_LOAD_COLOR;
|
||||||
|
|
||||||
struct FTContext {
|
struct FTContext {
|
||||||
FT_Library library;
|
FT_Library library;
|
||||||
@ -90,47 +90,70 @@ unsigned Font::width(String::Char c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<Image, Vec2I> Font::render(String::Char c) {
|
tuple<Image, Vec2I, bool> Font::render(String::Char c) {
|
||||||
if (!m_fontImpl)
|
if (!m_fontImpl)
|
||||||
throw FontException("Font::render called on uninitialized font.");
|
throw FontException("Font::render called on uninitialized font.");
|
||||||
|
|
||||||
FT_Face face = m_fontImpl->face;
|
FT_Face face = m_fontImpl->face;
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(face, c);
|
if (m_loadedPixelSize != m_pixelSize || m_loadedChar != c) {
|
||||||
if (FT_Load_Glyph(face, glyph_index, FontLoadFlags) != 0)
|
FT_UInt glyph_index = FT_Get_Char_Index(face, c);
|
||||||
return {};
|
if (FT_Load_Glyph(face, glyph_index, FontLoadFlags) != 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
/* convert to an anti-aliased bitmap */
|
/* convert to an anti-aliased bitmap */
|
||||||
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_loadedPixelSize = m_pixelSize;
|
||||||
|
m_loadedChar = m_loadedChar;
|
||||||
|
|
||||||
FT_GlyphSlot slot = face->glyph;
|
FT_GlyphSlot slot = face->glyph;
|
||||||
|
|
||||||
unsigned width = slot->bitmap.width;
|
unsigned width = slot->bitmap.width;
|
||||||
unsigned height = slot->bitmap.rows;
|
unsigned height = slot->bitmap.rows;
|
||||||
|
bool colored = false;
|
||||||
|
|
||||||
Image image(width + 2, height + 2, PixelFormat::RGBA32);
|
Image image(width + 2, height + 2, PixelFormat::BGRA32);
|
||||||
Vec4B white(255, 255, 255, 0);
|
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||||
image.fill(white);
|
Vec4B white(255, 255, 255, 0);
|
||||||
|
image.fill(white);
|
||||||
for (unsigned y = 0; y != height; ++y) {
|
for (unsigned y = 0; y != height; ++y) {
|
||||||
uint8_t* p = slot->bitmap.buffer + y * slot->bitmap.pitch;
|
uint8_t* p = slot->bitmap.buffer + y * slot->bitmap.pitch;
|
||||||
for (unsigned x = 0; x != width; ++x) {
|
for (unsigned x = 0; x != width; ++x) {
|
||||||
if (x < width && y < height) {
|
if (x < width && y < height) {
|
||||||
uint8_t value = *(p + x);
|
white[3] = m_alphaThreshold
|
||||||
if (m_alphaThreshold) {
|
? (*(p + x) >= m_alphaThreshold ? 255 : 0)
|
||||||
if (value >= m_alphaThreshold) {
|
: *(p + x);
|
||||||
white[3] = 255;
|
|
||||||
image.set(x + 1, height - y, white);
|
|
||||||
}
|
|
||||||
} else if (value) {
|
|
||||||
white[3] = value;
|
|
||||||
image.set(x + 1, height - y, white);
|
image.set(x + 1, height - y, white);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (colored = slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
|
||||||
|
unsigned bpp = image.bytesPerPixel();
|
||||||
|
uint8_t* data = image.data() + bpp + ((image.width() * (image.height() - 2)) * bpp); // offset by 1 pixel as it's padded
|
||||||
|
for (size_t y = 0; y != height; ++y) {
|
||||||
|
memcpy(data - (y * image.width() * bpp),
|
||||||
|
slot->bitmap.buffer + y * slot->bitmap.pitch,
|
||||||
|
slot->bitmap.pitch);
|
||||||
|
}
|
||||||
|
// unfortunately FreeType pre-multiplied the color channels :(
|
||||||
|
image.forEachPixel([](unsigned, unsigned, Vec4B& pixel) {
|
||||||
|
if (pixel[3] != 0 && pixel[3] != 255) {
|
||||||
|
float a = byteToFloat(pixel[3]);
|
||||||
|
pixel[0] /= a;
|
||||||
|
pixel[1] /= a;
|
||||||
|
pixel[2] /= a;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { std::move(image), {slot->bitmap_left - 1, (slot->bitmap_top - height) + (m_pixelSize / 4) - 1} };
|
return make_tuple(
|
||||||
|
std::move(image),
|
||||||
|
Vec2I(slot->bitmap_left - 1, (slot->bitmap_top - height) + (m_pixelSize / 4) - 1),
|
||||||
|
colored);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Font::exists(String::Char c) {
|
bool Font::exists(String::Char c) {
|
||||||
|
@ -34,13 +34,15 @@ public:
|
|||||||
// May return empty image on unrenderable character (Normally, this will
|
// May return empty image on unrenderable character (Normally, this will
|
||||||
// render a box, but if there is an internal freetype error this may return
|
// render a box, but if there is an internal freetype error this may return
|
||||||
// an empty image).
|
// an empty image).
|
||||||
std::pair<Image, Vec2I> render(String::Char c);
|
tuple<Image, Vec2I, bool> render(String::Char c);
|
||||||
bool exists(String::Char c);
|
bool exists(String::Char c);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FontImplPtr m_fontImpl;
|
FontImplPtr m_fontImpl;
|
||||||
ByteArrayConstPtr m_fontBuffer;
|
ByteArrayConstPtr m_fontBuffer;
|
||||||
unsigned m_pixelSize;
|
unsigned m_pixelSize;
|
||||||
|
unsigned m_loadedPixelSize;
|
||||||
|
String::Char m_loadedChar;
|
||||||
uint8_t m_alphaThreshold;
|
uint8_t m_alphaThreshold;
|
||||||
|
|
||||||
HashMap<pair<String::Char, unsigned>, unsigned> m_widthCache;
|
HashMap<pair<String::Char, unsigned>, unsigned> m_widthCache;
|
||||||
|
@ -90,7 +90,8 @@ void BaseScriptPane::tick(float dt) {
|
|||||||
bool BaseScriptPane::sendEvent(InputEvent const& event) {
|
bool BaseScriptPane::sendEvent(InputEvent const& event) {
|
||||||
// Intercept GuiClose before the canvas child so GuiClose always closes
|
// Intercept GuiClose before the canvas child so GuiClose always closes
|
||||||
// BaseScriptPanes without having to support it in the script.
|
// BaseScriptPanes without having to support it in the script.
|
||||||
if (context()->actions(event).contains(InterfaceAction::GuiClose)) {
|
if (context()->actions(event).contains(InterfaceAction::GuiClose)
|
||||||
|
&& m_config.getBool("dismissable", true)) {
|
||||||
dismiss();
|
dismiss();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) {
|
|||||||
m_chatVisTime = config.get("visTime").toFloat();
|
m_chatVisTime = config.get("visTime").toFloat();
|
||||||
m_fadeRate = config.get("fadeRate").toDouble();
|
m_fadeRate = config.get("fadeRate").toDouble();
|
||||||
m_chatHistoryLimit = config.get("chatHistoryLimit").toInt();
|
m_chatHistoryLimit = config.get("chatHistoryLimit").toInt();
|
||||||
|
m_chatFormatString = config.getString("chatFormatString");
|
||||||
|
|
||||||
m_portraitTextOffset = jsonToVec2I(config.get("portraitTextOffset"));
|
m_portraitTextOffset = jsonToVec2I(config.get("portraitTextOffset"));
|
||||||
m_portraitImageOffset = jsonToVec2I(config.get("portraitImageOffset"));
|
m_portraitImageOffset = jsonToVec2I(config.get("portraitImageOffset"));
|
||||||
@ -180,7 +181,7 @@ void Chat::addMessages(List<ChatReceivedMessage> const& messages, bool showPane)
|
|||||||
guiContext.setTextStyle(m_chatTextStyle);
|
guiContext.setTextStyle(m_chatTextStyle);
|
||||||
StringList lines;
|
StringList lines;
|
||||||
if (message.fromNick != "" && message.portrait == "")
|
if (message.fromNick != "" && message.portrait == "")
|
||||||
lines = guiContext.wrapInterfaceText(strf("<{}> {}", message.fromNick, message.text), wrapWidth);
|
lines = guiContext.wrapInterfaceText(strf(m_chatFormatString.utf8Ptr(), message.fromNick, message.text), wrapWidth);
|
||||||
else
|
else
|
||||||
lines = guiContext.wrapInterfaceText(message.text, wrapWidth);
|
lines = guiContext.wrapInterfaceText(message.text, wrapWidth);
|
||||||
|
|
||||||
@ -203,7 +204,8 @@ void Chat::addMessages(List<ChatReceivedMessage> const& messages, bool showPane)
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_receivedMessages.resize(std::min((unsigned)m_receivedMessages.size(), m_chatHistoryLimit));
|
if ((unsigned)m_receivedMessages.size() > m_chatHistoryLimit)
|
||||||
|
m_receivedMessages.resize(m_chatHistoryLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::addHistory(String const& chat) {
|
void Chat::addHistory(String const& chat) {
|
||||||
@ -216,6 +218,13 @@ void Chat::addHistory(String const& chat) {
|
|||||||
m_client->playerStorage()->setMetadata("chatHistory", JsonArray::from(m_chatHistory));
|
m_client->playerStorage()->setMetadata("chatHistory", JsonArray::from(m_chatHistory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chat::clear(size_t count) {
|
||||||
|
if (count > m_receivedMessages.size())
|
||||||
|
m_receivedMessages.clear();
|
||||||
|
else
|
||||||
|
m_receivedMessages.erase(m_receivedMessages.begin(), m_receivedMessages.begin() + count);
|
||||||
|
}
|
||||||
|
|
||||||
void Chat::renderImpl() {
|
void Chat::renderImpl() {
|
||||||
Pane::renderImpl();
|
Pane::renderImpl();
|
||||||
if (m_textBox->hasFocus())
|
if (m_textBox->hasFocus())
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
void addLine(String const& text, bool showPane = true);
|
void addLine(String const& text, bool showPane = true);
|
||||||
void addMessages(List<ChatReceivedMessage> const& messages, bool showPane = true);
|
void addMessages(List<ChatReceivedMessage> const& messages, bool showPane = true);
|
||||||
void addHistory(String const& chat);
|
void addHistory(String const& chat);
|
||||||
|
void clear(size_t count = std::numeric_limits<size_t>::max());
|
||||||
|
|
||||||
String currentChat() const;
|
String currentChat() const;
|
||||||
bool setCurrentChat(String const& chat, bool moveCursor = false);
|
bool setCurrentChat(String const& chat, bool moveCursor = false);
|
||||||
@ -69,6 +70,7 @@ private:
|
|||||||
TextStyle m_chatTextStyle;
|
TextStyle m_chatTextStyle;
|
||||||
unsigned m_chatHistoryLimit;
|
unsigned m_chatHistoryLimit;
|
||||||
int m_historyOffset;
|
int m_historyOffset;
|
||||||
|
String m_chatFormatString;
|
||||||
|
|
||||||
CanvasWidgetPtr m_chatLog;
|
CanvasWidgetPtr m_chatLog;
|
||||||
Vec2I m_chatLogPadding;
|
Vec2I m_chatLogPadding;
|
||||||
|
@ -13,6 +13,11 @@ namespace Star {
|
|||||||
LuaCallbacks LuaBindings::makeInterfaceCallbacks(MainInterface* mainInterface) {
|
LuaCallbacks LuaBindings::makeInterfaceCallbacks(MainInterface* mainInterface) {
|
||||||
LuaCallbacks callbacks;
|
LuaCallbacks callbacks;
|
||||||
|
|
||||||
|
callbacks.registerCallbackWithSignature<bool>(
|
||||||
|
"hudVisible", bind(mem_fn(&MainInterface::hudVisible), mainInterface));
|
||||||
|
callbacks.registerCallbackWithSignature<void, bool>(
|
||||||
|
"setHudVisible", bind(mem_fn(&MainInterface::setHudVisible), mainInterface, _1));
|
||||||
|
|
||||||
callbacks.registerCallback("bindCanvas", [mainInterface](String const& canvasName, Maybe<bool> ignoreInterfaceScale) -> Maybe<CanvasWidgetPtr> {
|
callbacks.registerCallback("bindCanvas", [mainInterface](String const& canvasName, Maybe<bool> ignoreInterfaceScale) -> Maybe<CanvasWidgetPtr> {
|
||||||
if (auto canvas = mainInterface->fetchCanvas(canvasName, ignoreInterfaceScale.value(false)))
|
if (auto canvas = mainInterface->fetchCanvas(canvasName, ignoreInterfaceScale.value(false)))
|
||||||
return canvas;
|
return canvas;
|
||||||
@ -87,6 +92,10 @@ LuaCallbacks LuaBindings::makeChatCallbacks(MainInterface* mainInterface, Univer
|
|||||||
return chat->setCurrentChat(text, moveCursor.value(false));
|
return chat->setCurrentChat(text, moveCursor.value(false));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callbacks.registerCallback("clear", [chat](Maybe<size_t> count) {
|
||||||
|
chat->clear(count.value(std::numeric_limits<size_t>::max()));
|
||||||
|
});
|
||||||
|
|
||||||
return callbacks;
|
return callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,6 +893,14 @@ bool MainInterface::fixedCamera() const {
|
|||||||
return m_clientCommandProcessor->fixedCameraEnabled();
|
return m_clientCommandProcessor->fixedCameraEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainInterface::hudVisible() const {
|
||||||
|
return !m_disableHud;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainInterface::setHudVisible(bool visible) {
|
||||||
|
m_disableHud = !visible;
|
||||||
|
}
|
||||||
|
|
||||||
void MainInterface::warpToOrbitedWorld(bool deploy) {
|
void MainInterface::warpToOrbitedWorld(bool deploy) {
|
||||||
if (m_client->canBeamDown(deploy)) {
|
if (m_client->canBeamDown(deploy)) {
|
||||||
if (deploy)
|
if (deploy)
|
||||||
|
@ -100,7 +100,6 @@ public:
|
|||||||
|
|
||||||
Vec2F cursorWorldPosition() const;
|
Vec2F cursorWorldPosition() const;
|
||||||
|
|
||||||
void toggleDebugDisplay();
|
|
||||||
bool isDebugDisplayed();
|
bool isDebugDisplayed();
|
||||||
|
|
||||||
void doChat(String const& chat, bool addToHistory);
|
void doChat(String const& chat, bool addToHistory);
|
||||||
@ -112,6 +111,8 @@ public:
|
|||||||
void queueJoinRequest(pair<String, RpcPromiseKeeper<P2PJoinRequestReply>> request);
|
void queueJoinRequest(pair<String, RpcPromiseKeeper<P2PJoinRequestReply>> request);
|
||||||
|
|
||||||
bool fixedCamera() const;
|
bool fixedCamera() const;
|
||||||
|
bool hudVisible() const;
|
||||||
|
void setHudVisible(bool visible = true);
|
||||||
|
|
||||||
void warpToOrbitedWorld(bool deploy = false);
|
void warpToOrbitedWorld(bool deploy = false);
|
||||||
void warpToOwnShip();
|
void warpToOwnShip();
|
||||||
@ -209,7 +210,7 @@ private:
|
|||||||
QuestIndicatorPainterPtr m_questIndicatorPainter;
|
QuestIndicatorPainterPtr m_questIndicatorPainter;
|
||||||
ChatBubbleManagerPtr m_chatBubbleManager;
|
ChatBubbleManagerPtr m_chatBubbleManager;
|
||||||
|
|
||||||
bool m_disableHud{false};
|
bool m_disableHud = false;
|
||||||
|
|
||||||
String m_lastCommand;
|
String m_lastCommand;
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ Maybe<Uuid> PlayerStorage::playerUuidByName(String const& name, Maybe<Uuid> exce
|
|||||||
|
|
||||||
RecursiveMutexLocker locker(m_mutex);
|
RecursiveMutexLocker locker(m_mutex);
|
||||||
|
|
||||||
size_t longest = SIZE_MAX;
|
size_t longest = std::numeric_limits<size_t>::max();
|
||||||
for (auto& cache : m_savedPlayersCache) {
|
for (auto& cache : m_savedPlayersCache) {
|
||||||
if (except && *except == cache.first)
|
if (except && *except == cache.first)
|
||||||
continue;
|
continue;
|
||||||
|
@ -14,13 +14,13 @@ void FontTextureGroup::cleanup(int64_t timeout) {
|
|||||||
|
|
||||||
void FontTextureGroup::switchFont(String const& font) {
|
void FontTextureGroup::switchFont(String const& font) {
|
||||||
if (font.empty()) {
|
if (font.empty()) {
|
||||||
m_font = m_defaultFont;
|
m_activeFont = m_defaultFont;
|
||||||
m_fontName.clear();
|
m_fontName.clear();
|
||||||
}
|
}
|
||||||
else if (m_fontName != font) {
|
else if (m_fontName != font) {
|
||||||
m_fontName = font;
|
m_fontName = font;
|
||||||
auto find = m_fonts.find(font);
|
auto find = m_fonts.find(font);
|
||||||
m_font = find != m_fonts.end() ? find->second : m_defaultFont;
|
m_activeFont = find != m_fonts.end() ? find->second : m_defaultFont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,31 +28,35 @@ String const& FontTextureGroup::activeFont() {
|
|||||||
return m_fontName;
|
return m_fontName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureGroup::addFont(FontPtr const& font, String const& name, bool isDefault) {
|
void FontTextureGroup::addFont(FontPtr const& font, String const& name) {
|
||||||
m_fonts[name] = font;
|
m_fonts[name] = font;
|
||||||
if (isDefault)
|
|
||||||
m_defaultFont = m_font = font;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureGroup::clearFonts() {
|
void FontTextureGroup::clearFonts() {
|
||||||
m_fonts.clear();
|
m_fonts.clear();
|
||||||
m_font = m_defaultFont;
|
m_activeFont = m_defaultFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureGroup::setFallbackFont(String const& fontName) {
|
void FontTextureGroup::setFixedFonts(String const& defaultFontName, String const& fallbackFontName, String const& emojiFontName) {
|
||||||
if (auto font = m_fonts.ptr(fontName))
|
if (auto defaultFont = m_fonts.ptr(defaultFontName))
|
||||||
m_fallbackFont = *font;
|
m_defaultFont = m_activeFont = *defaultFont;
|
||||||
|
if (auto fallbackFont = m_fonts.ptr(fallbackFontName))
|
||||||
|
m_fallbackFont = *fallbackFont;
|
||||||
|
if (auto emojiFont = m_fonts.ptr(emojiFontName))
|
||||||
|
m_emojiFont = *emojiFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Char c, unsigned size, Directives const* processingDirectives)
|
const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Char c, unsigned size, Directives const* processingDirectives)
|
||||||
{
|
{
|
||||||
Font* font = (m_font->exists(c) || !m_fallbackFont) ? m_font.get() : m_fallbackFont.get();
|
Font* font = getFontForCharacter(c);
|
||||||
|
if (font == m_emojiFont.get())
|
||||||
|
processingDirectives = nullptr;
|
||||||
auto res = m_glyphs.insert(GlyphDescriptor{c, size, processingDirectives ? processingDirectives->hash() : 0, font}, GlyphTexture());
|
auto res = m_glyphs.insert(GlyphDescriptor{c, size, processingDirectives ? processingDirectives->hash() : 0, font}, GlyphTexture());
|
||||||
|
auto& glyphTexture = res.first->second;
|
||||||
if (res.second) {
|
if (res.second) {
|
||||||
font->setPixelSize(size);
|
font->setPixelSize(size);
|
||||||
auto pair = font->render(c);
|
auto renderResult = font->render(c);
|
||||||
Image& image = pair.first;
|
Image& image = get<0>(renderResult);
|
||||||
if (processingDirectives) {
|
if (processingDirectives) {
|
||||||
try {
|
try {
|
||||||
Directives const& directives = *processingDirectives;
|
Directives const& directives = *processingDirectives;
|
||||||
@ -61,7 +65,7 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
|||||||
for (auto& entry : directives->entries)
|
for (auto& entry : directives->entries)
|
||||||
processImageOperation(entry.operation, image);
|
processImageOperation(entry.operation, image);
|
||||||
|
|
||||||
res.first->second.offset = (preSize - Vec2F(image.size())) / 2;
|
glyphTexture.offset = (preSize - Vec2F(image.size())) / 2;
|
||||||
}
|
}
|
||||||
catch (StarException const&) {
|
catch (StarException const&) {
|
||||||
image.forEachPixel([](unsigned x, unsigned y, Vec4B& pixel) {
|
image.forEachPixel([](unsigned x, unsigned y, Vec4B& pixel) {
|
||||||
@ -69,15 +73,14 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
res.first->second.offset = Vec2F();
|
|
||||||
|
|
||||||
res.first->second.offset += Vec2F(pair.second);
|
glyphTexture.colored = get<2>(renderResult);
|
||||||
res.first->second.texture = m_textureGroup->create(image);
|
glyphTexture.offset += Vec2F(get<1>(renderResult));
|
||||||
|
glyphTexture.texture = m_textureGroup->create(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.first->second.time = Time::monotonicMilliseconds();
|
glyphTexture.time = Time::monotonicMilliseconds();
|
||||||
return res.first->second;
|
return glyphTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr FontTextureGroup::glyphTexturePtr(String::Char c, unsigned size) {
|
TexturePtr FontTextureGroup::glyphTexturePtr(String::Char c, unsigned size) {
|
||||||
@ -89,9 +92,31 @@ TexturePtr FontTextureGroup::glyphTexturePtr(String::Char c, unsigned size, Dire
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned FontTextureGroup::glyphWidth(String::Char c, unsigned fontSize) {
|
unsigned FontTextureGroup::glyphWidth(String::Char c, unsigned fontSize) {
|
||||||
Font* font = (m_font->exists(c) || !m_fallbackFont) ? m_font.get() : m_fallbackFont.get();
|
Font* font = getFontForCharacter(c);
|
||||||
font->setPixelSize(fontSize);
|
font->setPixelSize(fontSize);
|
||||||
return font->width(c);
|
return font->width(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Font* FontTextureGroup::getFontForCharacter(String::Char c) {
|
||||||
|
if (((c >= 0x1F600 && c <= 0x1F64F) || // Emoticons
|
||||||
|
(c >= 0x1F300 && c <= 0x1F5FF) || // Misc Symbols and Pictographs
|
||||||
|
(c >= 0x1F680 && c <= 0x1F6FF) || // Transport and Map
|
||||||
|
(c >= 0x1F1E6 && c <= 0x1F1FF) || // Regional country flags
|
||||||
|
(c >= 0x2600 && c <= 0x26FF ) || // Misc symbols 9728 - 9983
|
||||||
|
(c >= 0x2700 && c <= 0x27BF ) || // Dingbats
|
||||||
|
(c >= 0xFE00 && c <= 0xFE0F ) || // Variation Selectors
|
||||||
|
(c >= 0x1F900 && c <= 0x1F9FF) || // Supplemental Symbols and Pictographs
|
||||||
|
(c >= 0x1F018 && c <= 0x1F270) || // Various asian characters
|
||||||
|
(c >= 65024 && c <= 65039 ) || // Variation selector
|
||||||
|
(c >= 9100 && c <= 9300 ) || // Misc items
|
||||||
|
(c >= 8400 && c <= 8447 ))&& // Combining Diacritical Marks for Symbols
|
||||||
|
m_emojiFont->exists(c)
|
||||||
|
)
|
||||||
|
return m_emojiFont.get();
|
||||||
|
else if (m_activeFont->exists(c) || !m_fallbackFont)
|
||||||
|
return m_activeFont.get();
|
||||||
|
else
|
||||||
|
return m_fallbackFont.get();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ public:
|
|||||||
|
|
||||||
struct GlyphTexture {
|
struct GlyphTexture {
|
||||||
TexturePtr texture;
|
TexturePtr texture;
|
||||||
int64_t time;
|
bool colored = false;
|
||||||
|
int64_t time = 0;
|
||||||
Vec2F offset;
|
Vec2F offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,16 +36,18 @@ public:
|
|||||||
// Switches the current font
|
// Switches the current font
|
||||||
void switchFont(String const& font);
|
void switchFont(String const& font);
|
||||||
String const& activeFont();
|
String const& activeFont();
|
||||||
void addFont(FontPtr const& font, String const& name, bool isDefault = false);
|
void addFont(FontPtr const& font, String const& name);
|
||||||
void clearFonts();
|
void clearFonts();
|
||||||
void setFallbackFont(String const& fontName);
|
void setFixedFonts(String const& defaultFontName, String const& fallbackFontName, String const& emojiFontName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Font* getFontForCharacter(String::Char);
|
||||||
|
|
||||||
CaseInsensitiveStringMap<FontPtr> m_fonts;
|
CaseInsensitiveStringMap<FontPtr> m_fonts;
|
||||||
String m_fontName;
|
String m_fontName;
|
||||||
FontPtr m_font;
|
FontPtr m_activeFont;
|
||||||
FontPtr m_defaultFont;
|
FontPtr m_defaultFont;
|
||||||
FontPtr m_fallbackFont;
|
FontPtr m_fallbackFont;
|
||||||
|
FontPtr m_emojiFont;
|
||||||
|
|
||||||
TextureGroupPtr m_textureGroup;
|
TextureGroupPtr m_textureGroup;
|
||||||
HashMap<GlyphDescriptor, GlyphTexture> m_glyphs;
|
HashMap<GlyphDescriptor, GlyphTexture> m_glyphs;
|
||||||
|
@ -320,14 +320,9 @@ void TextPainter::reloadFonts() {
|
|||||||
m_fontTextureGroup.clearFonts();
|
m_fontTextureGroup.clearFonts();
|
||||||
m_fontTextureGroup.cleanup(0);
|
m_fontTextureGroup.cleanup(0);
|
||||||
auto assets = Root::singleton().assets();
|
auto assets = Root::singleton().assets();
|
||||||
String defaultName = "hobo";
|
|
||||||
auto defaultFont = loadFont("/hobo.ttf", defaultName);
|
|
||||||
auto loadFontsByExtension = [&](String const& ext) {
|
auto loadFontsByExtension = [&](String const& ext) {
|
||||||
for (auto& fontPath : assets->scanExtension(ext)) {
|
for (auto& fontPath : assets->scanExtension(ext)) {
|
||||||
auto font = assets->font(fontPath);
|
auto font = assets->font(fontPath);
|
||||||
if (font == defaultFont)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto name = AssetPath::filename(fontPath);
|
auto name = AssetPath::filename(fontPath);
|
||||||
name = name.substr(0, name.findLast("."));
|
name = name.substr(0, name.findLast("."));
|
||||||
addFont(loadFont(fontPath, name), name);
|
addFont(loadFont(fontPath, name), name);
|
||||||
@ -335,8 +330,10 @@ void TextPainter::reloadFonts() {
|
|||||||
};
|
};
|
||||||
loadFontsByExtension("ttf");
|
loadFontsByExtension("ttf");
|
||||||
loadFontsByExtension("woff2");
|
loadFontsByExtension("woff2");
|
||||||
m_fontTextureGroup.addFont(defaultFont, defaultName, true);
|
m_fontTextureGroup.setFixedFonts(
|
||||||
m_fontTextureGroup.setFallbackFont("unifont");
|
assets->json("/interface.config:font.defaultFont").toString(),
|
||||||
|
assets->json("/interface.config:font.fallbackFont").toString(),
|
||||||
|
assets->json("/interface.config:font.emojiFont").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextPainter::cleanup(int64_t timeout) {
|
void TextPainter::cleanup(int64_t timeout) {
|
||||||
@ -518,13 +515,15 @@ void TextPainter::renderPrimitives() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize,
|
void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize,
|
||||||
float scale, Vec4B const& color, Directives const* processingDirectives) {
|
float scale, Vec4B color, Directives const* processingDirectives) {
|
||||||
if (!fontSize)
|
if (!fontSize)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives);
|
const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives);
|
||||||
Vec2F offset = glyphTexture.offset * scale;
|
if (glyphTexture.colored)
|
||||||
out.emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, Vec2F::round(screenPos + offset), scale, color, 0.0f);
|
color[0] = color[1] = color[2] = 255;
|
||||||
|
out.emplace_back(std::in_place_type_t<RenderQuad>(),
|
||||||
|
glyphTexture.texture, Vec2F::round(screenPos + glyphTexture.offset * scale), scale, color, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
FontPtr TextPainter::loadFont(String const& fontPath, Maybe<String> fontName) {
|
FontPtr TextPainter::loadFont(String const& fontPath, Maybe<String> fontName) {
|
||||||
|
@ -86,7 +86,7 @@ private:
|
|||||||
RectF doRenderGlyph(String::Char c, TextPositioning const& position, bool reallyRender);
|
RectF doRenderGlyph(String::Char c, TextPositioning const& position, bool reallyRender);
|
||||||
|
|
||||||
void renderPrimitives();
|
void renderPrimitives();
|
||||||
void renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives = nullptr);
|
void renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize, float scale, Vec4B color, Directives const* processingDirectives = nullptr);
|
||||||
static FontPtr loadFont(String const& fontPath, Maybe<String> fontName = {});
|
static FontPtr loadFont(String const& fontPath, Maybe<String> fontName = {});
|
||||||
|
|
||||||
RendererPtr m_renderer;
|
RendererPtr m_renderer;
|
||||||
|
Loading…
Reference in New Issue
Block a user