mirror of https://github.com/aseprite/aseprite.git
Add option to switch font hinting (fix #4931, aseprite/laf#138)
This commit is contained in:
parent
d8632b6208
commit
b3f4e37b69
|
@ -785,6 +785,11 @@ load = Load External Font
|
|||
select_truetype_fonts = Select a Font File
|
||||
empty_fonts = No system fonts were found
|
||||
|
||||
[font_style]
|
||||
antialias = Antialias
|
||||
hinting = Hinting
|
||||
ligatures = Ligatures
|
||||
|
||||
[frame_combo]
|
||||
all_frames = All frames
|
||||
selected_frames = Selected frames
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2025 by Igara Studio S.A. -->
|
||||
<gui>
|
||||
<vbox id="font_style">
|
||||
<check id="antialias" text="@.antialias" />
|
||||
<check id="hinting" text="@.hinting" />
|
||||
<separator horizontal="true" />
|
||||
<check id="ligatures" text="@.ligatures" />
|
||||
</vbox>
|
||||
</gui>
|
|
@ -32,17 +32,11 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
|
|||
{
|
||||
ASSERT(fontMgr);
|
||||
|
||||
// Use cache
|
||||
if (m_antialias) {
|
||||
auto it = m_antialiasFonts.find(size);
|
||||
if (it != m_antialiasFonts.end())
|
||||
return it->second;
|
||||
}
|
||||
else {
|
||||
auto it = m_fonts.find(size);
|
||||
if (it != m_fonts.end())
|
||||
return it->second;
|
||||
}
|
||||
// Use cached fonts
|
||||
const Cache::Key cacheKey{ size, m_antialias, m_hinting != text::FontHinting::None };
|
||||
auto it = m_cache.fonts.find(cacheKey);
|
||||
if (it != m_cache.fonts.end())
|
||||
return it->second; // Cache hit
|
||||
|
||||
text::FontRef font = nullptr;
|
||||
|
||||
|
@ -55,12 +49,18 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
|
|||
break;
|
||||
case text::FontType::FreeType: {
|
||||
font = fontMgr->loadTrueTypeFont(m_filename.c_str(), size);
|
||||
if (font)
|
||||
if (font) {
|
||||
font->setAntialias(m_antialias);
|
||||
font->setHinting(m_hinting);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache this font
|
||||
m_cache.fonts[cacheKey] = font;
|
||||
|
||||
// Load fallback
|
||||
if (m_fallback) {
|
||||
text::FontRef fallback = m_fallback->getFont(fontMgr, m_fallbackSize);
|
||||
if (font)
|
||||
|
@ -68,12 +68,6 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
|
|||
else
|
||||
return fallback; // Don't double-cache the fallback font
|
||||
}
|
||||
|
||||
// Cache this font
|
||||
if (m_antialias)
|
||||
m_antialiasFonts[size] = font;
|
||||
else
|
||||
m_fonts[size] = font;
|
||||
return font;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,25 @@ public:
|
|||
const std::string& filename() const { return m_filename; }
|
||||
|
||||
private:
|
||||
// Cache of loaded fonts so we avoid re-loading them.
|
||||
struct Cache {
|
||||
struct Key {
|
||||
float size;
|
||||
bool antialias : 1;
|
||||
bool hinting : 1;
|
||||
bool operator<(const Key& b) const
|
||||
{
|
||||
return size < b.size || antialias < b.antialias || hinting < b.hinting;
|
||||
}
|
||||
};
|
||||
std::map<Key, text::FontRef> fonts;
|
||||
};
|
||||
|
||||
text::FontType m_type;
|
||||
std::string m_filename;
|
||||
bool m_antialias;
|
||||
std::map<float, text::FontRef> m_fonts; // key=font size, value=real font
|
||||
std::map<float, text::FontRef> m_antialiasFonts;
|
||||
text::FontHinting m_hinting = text::FontHinting::Normal;
|
||||
Cache m_cache;
|
||||
FontData* m_fallback;
|
||||
float m_fallbackSize;
|
||||
float m_descent = 0.0f;
|
||||
|
|
|
@ -27,24 +27,28 @@ FontInfo::FontInfo(Type type,
|
|||
const std::string& name,
|
||||
const float size,
|
||||
const text::FontStyle style,
|
||||
const Flags flags)
|
||||
const Flags flags,
|
||||
const text::FontHinting hinting)
|
||||
: m_type(type)
|
||||
, m_name(name)
|
||||
, m_size(size)
|
||||
, m_style(style)
|
||||
, m_flags(flags)
|
||||
, m_hinting(hinting)
|
||||
{
|
||||
}
|
||||
|
||||
FontInfo::FontInfo(const FontInfo& other,
|
||||
const float size,
|
||||
const text::FontStyle style,
|
||||
const Flags flags)
|
||||
const Flags flags,
|
||||
text::FontHinting hinting)
|
||||
: m_type(other.type())
|
||||
, m_name(other.name())
|
||||
, m_size(size)
|
||||
, m_style(style)
|
||||
, m_flags(flags)
|
||||
, m_hinting(hinting)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -130,6 +134,12 @@ std::string FontInfo::humanString() const
|
|||
result += " Antialias";
|
||||
if (ligatures())
|
||||
result += " Ligatures";
|
||||
switch (hinting()) {
|
||||
case text::FontHinting::None: result += " No Hinting"; break;
|
||||
case text::FontHinting::Slight: result += " Slight Hinting"; break;
|
||||
case text::FontHinting::Normal: break;
|
||||
case text::FontHinting::Full: result += " Full Hinting"; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -150,6 +160,7 @@ app::FontInfo convert_to(const std::string& from)
|
|||
bool bold = false;
|
||||
bool italic = false;
|
||||
app::FontInfo::Flags flags = app::FontInfo::Flags::None;
|
||||
text::FontHinting hinting = text::FontHinting::Normal;
|
||||
|
||||
if (!parts.empty()) {
|
||||
if (parts[0].compare(0, 5, "file=") == 0) {
|
||||
|
@ -176,6 +187,17 @@ app::FontInfo convert_to(const std::string& from)
|
|||
else if (parts[i].compare(0, 5, "size=") == 0) {
|
||||
size = std::strtof(parts[i].substr(5).c_str(), nullptr);
|
||||
}
|
||||
else if (parts[i].compare(0, 8, "hinting=") == 0) {
|
||||
std::string hintingStr = parts[i].substr(8);
|
||||
if (hintingStr == "none")
|
||||
hinting = text::FontHinting::None;
|
||||
else if (hintingStr == "slight")
|
||||
hinting = text::FontHinting::Slight;
|
||||
else if (hintingStr == "normal")
|
||||
hinting = text::FontHinting::Normal;
|
||||
else if (hintingStr == "full")
|
||||
hinting = text::FontHinting::Full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,7 +209,7 @@ app::FontInfo convert_to(const std::string& from)
|
|||
else if (italic)
|
||||
style = text::FontStyle::Italic();
|
||||
|
||||
return app::FontInfo(type, name, size, style, flags);
|
||||
return app::FontInfo(type, name, size, style, flags, hinting);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -213,6 +235,17 @@ std::string convert_to(const app::FontInfo& from)
|
|||
result += ",antialias";
|
||||
if (from.ligatures())
|
||||
result += ",ligatures";
|
||||
if (from.hinting() != text::FontHinting::Normal) {
|
||||
result += ",hinting=";
|
||||
switch (from.hinting()) {
|
||||
case text::FontHinting::None: result += "none"; break;
|
||||
case text::FontHinting::Slight: result += "slight"; break;
|
||||
case text::FontHinting::Normal:
|
||||
// Filtered out by above if
|
||||
break;
|
||||
case text::FontHinting::Full: result += "full"; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "base/convert_to.h"
|
||||
#include "base/enum_flags.h"
|
||||
#include "text/font_hinting.h"
|
||||
#include "text/font_style.h"
|
||||
#include "text/font_type.h"
|
||||
#include "text/fwd.h"
|
||||
|
@ -49,9 +50,14 @@ public:
|
|||
const std::string& name = {},
|
||||
float size = kDefaultSize,
|
||||
text::FontStyle style = text::FontStyle(),
|
||||
Flags flags = Flags::None);
|
||||
Flags flags = Flags::None,
|
||||
text::FontHinting hinting = text::FontHinting::Normal);
|
||||
|
||||
FontInfo(const FontInfo& other, float size, text::FontStyle style, Flags flags);
|
||||
FontInfo(const FontInfo& other,
|
||||
float size,
|
||||
text::FontStyle style,
|
||||
Flags flags,
|
||||
text::FontHinting hinting);
|
||||
|
||||
bool isValid() const { return m_type != Type::Unknown; }
|
||||
bool useDefaultSize() const { return m_size == kDefaultSize; }
|
||||
|
@ -72,6 +78,7 @@ public:
|
|||
Flags flags() const { return m_flags; }
|
||||
bool antialias() const;
|
||||
bool ligatures() const;
|
||||
text::FontHinting hinting() const { return m_hinting; }
|
||||
|
||||
text::TypefaceRef findTypeface(const text::FontMgrRef& fontMgr) const;
|
||||
|
||||
|
@ -92,6 +99,7 @@ private:
|
|||
float m_size = kDefaultSize;
|
||||
text::FontStyle m_style;
|
||||
Flags m_flags = Flags::None;
|
||||
text::FontHinting m_hinting = text::FontHinting::Normal;
|
||||
};
|
||||
|
||||
LAF_ENUM_FLAGS(FontInfo::Flags);
|
||||
|
|
|
@ -82,8 +82,10 @@ text::FontRef Fonts::fontFromInfo(const FontInfo& fontInfo)
|
|||
}
|
||||
}
|
||||
|
||||
if (font)
|
||||
if (font) {
|
||||
font->setAntialias(fontInfo.antialias());
|
||||
font->setHinting(fontInfo.hinting());
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,13 @@
|
|||
#include "base/scoped_value.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ui/display.h"
|
||||
#include "ui/fit_bounds.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/scale.h"
|
||||
|
||||
#include "font_style.xml.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
@ -248,31 +251,22 @@ void FontEntry::FontSize::onEntryChange()
|
|||
Change();
|
||||
}
|
||||
|
||||
FontEntry::FontStyle::FontStyle() : ButtonSet(2, true)
|
||||
FontEntry::FontStyle::FontStyle() : ButtonSet(3, true)
|
||||
{
|
||||
addItem("B");
|
||||
addItem("I");
|
||||
addItem("...");
|
||||
setMultiMode(MultiMode::Set);
|
||||
}
|
||||
|
||||
FontEntry::FontLigatures::FontLigatures() : ButtonSet(1, true)
|
||||
{
|
||||
addItem("fi");
|
||||
setMultiMode(MultiMode::Set);
|
||||
}
|
||||
|
||||
FontEntry::FontEntry() : m_antialias("Antialias")
|
||||
FontEntry::FontEntry()
|
||||
{
|
||||
m_face.setExpansive(true);
|
||||
m_size.setExpansive(false);
|
||||
m_style.setExpansive(false);
|
||||
m_ligatures.setExpansive(false);
|
||||
m_antialias.setExpansive(false);
|
||||
addChild(&m_face);
|
||||
addChild(&m_size);
|
||||
addChild(&m_style);
|
||||
addChild(&m_ligatures);
|
||||
addChild(&m_antialias);
|
||||
|
||||
m_face.setMinSize(gfx::Size(128 * guiscale(), 0));
|
||||
|
||||
|
@ -280,52 +274,20 @@ FontEntry::FontEntry() : m_antialias("Antialias")
|
|||
if (newTypeName.size() > 0)
|
||||
setInfo(newTypeName, from);
|
||||
else {
|
||||
setInfo(FontInfo(newTypeName, m_info.size(), m_info.style(), m_info.flags()), from);
|
||||
setInfo(
|
||||
FontInfo(newTypeName, m_info.size(), m_info.style(), m_info.flags(), m_info.hinting()),
|
||||
from);
|
||||
}
|
||||
invalidate();
|
||||
});
|
||||
|
||||
m_size.Change.connect([this]() {
|
||||
const float newSize = std::strtof(m_size.getValue().c_str(), nullptr);
|
||||
setInfo(FontInfo(m_info, newSize, m_info.style(), m_info.flags()), From::Size);
|
||||
setInfo(FontInfo(m_info, newSize, m_info.style(), m_info.flags(), m_info.hinting()),
|
||||
From::Size);
|
||||
});
|
||||
|
||||
m_style.ItemChange.connect([this](ButtonSet::Item* item) {
|
||||
text::FontStyle style = m_info.style();
|
||||
switch (m_style.getItemIndex(item)) {
|
||||
// Bold button changed
|
||||
case 0: {
|
||||
const bool bold = m_style.getItem(0)->isSelected();
|
||||
style = text::FontStyle(
|
||||
bold ? text::FontStyle::Weight::Bold : text::FontStyle::Weight::Normal,
|
||||
style.width(),
|
||||
style.slant());
|
||||
break;
|
||||
}
|
||||
// Italic button changed
|
||||
case 1: {
|
||||
const bool italic = m_style.getItem(1)->isSelected();
|
||||
style = text::FontStyle(
|
||||
style.weight(),
|
||||
style.width(),
|
||||
italic ? text::FontStyle::Slant::Italic : text::FontStyle::Slant::Upright);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags()), From::Style);
|
||||
});
|
||||
|
||||
auto flagsChange = [this]() {
|
||||
FontInfo::Flags flags = FontInfo::Flags::None;
|
||||
if (m_antialias.isSelected())
|
||||
flags |= FontInfo::Flags::Antialias;
|
||||
if (m_ligatures.getItem(0)->isSelected())
|
||||
flags |= FontInfo::Flags::Ligatures;
|
||||
setInfo(FontInfo(m_info, m_info.size(), m_info.style(), flags), From::Flags);
|
||||
};
|
||||
m_ligatures.ItemChange.connect(flagsChange);
|
||||
m_antialias.Click.connect(flagsChange);
|
||||
m_style.ItemChange.connect(&FontEntry::onStyleItemClick, this);
|
||||
}
|
||||
|
||||
// Defined here as FontPopup type is not fully defined in the header
|
||||
|
@ -351,12 +313,84 @@ void FontEntry::setInfo(const FontInfo& info, const From fromField)
|
|||
m_style.getItem(1)->setSelected(info.style().slant() != text::FontStyle::Slant::Upright);
|
||||
}
|
||||
|
||||
if (fromField != From::Flags) {
|
||||
m_ligatures.getItem(0)->setSelected(info.ligatures());
|
||||
m_antialias.setSelected(info.antialias());
|
||||
}
|
||||
|
||||
FontChange(m_info, fromField);
|
||||
}
|
||||
|
||||
void FontEntry::onStyleItemClick(ButtonSet::Item* item)
|
||||
{
|
||||
text::FontStyle style = m_info.style();
|
||||
|
||||
switch (m_style.getItemIndex(item)) {
|
||||
// Bold button changed
|
||||
case 0: {
|
||||
const bool bold = m_style.getItem(0)->isSelected();
|
||||
style = text::FontStyle(
|
||||
bold ? text::FontStyle::Weight::Bold : text::FontStyle::Weight::Normal,
|
||||
style.width(),
|
||||
style.slant());
|
||||
|
||||
setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags(), m_info.hinting()),
|
||||
From::Style);
|
||||
break;
|
||||
}
|
||||
// Italic button changed
|
||||
case 1: {
|
||||
const bool italic = m_style.getItem(1)->isSelected();
|
||||
style = text::FontStyle(
|
||||
style.weight(),
|
||||
style.width(),
|
||||
italic ? text::FontStyle::Slant::Italic : text::FontStyle::Slant::Upright);
|
||||
|
||||
setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags(), m_info.hinting()),
|
||||
From::Style);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
item->setSelected(false); // Unselect the "..." button
|
||||
|
||||
ui::PopupWindow popup;
|
||||
app::gen::FontStyle content;
|
||||
|
||||
content.antialias()->setSelected(m_info.antialias());
|
||||
content.ligatures()->setSelected(m_info.ligatures());
|
||||
content.hinting()->setSelected(m_info.hinting() == text::FontHinting::Normal);
|
||||
|
||||
auto flagsChange = [this, &content]() {
|
||||
FontInfo::Flags flags = FontInfo::Flags::None;
|
||||
if (content.antialias()->isSelected())
|
||||
flags |= FontInfo::Flags::Antialias;
|
||||
if (content.ligatures()->isSelected())
|
||||
flags |= FontInfo::Flags::Ligatures;
|
||||
setInfo(FontInfo(m_info, m_info.size(), m_info.style(), flags, m_info.hinting()),
|
||||
From::Flags);
|
||||
};
|
||||
|
||||
auto hintingChange = [this, &content]() {
|
||||
auto hinting = (content.hinting()->isSelected() ? text::FontHinting::Normal :
|
||||
text::FontHinting::None);
|
||||
|
||||
setInfo(FontInfo(m_info, m_info.size(), m_info.style(), m_info.flags(), hinting),
|
||||
From::Hinting);
|
||||
};
|
||||
|
||||
content.antialias()->Click.connect(flagsChange);
|
||||
content.ligatures()->Click.connect(flagsChange);
|
||||
content.hinting()->Click.connect(hintingChange);
|
||||
|
||||
popup.addChild(&content);
|
||||
popup.remapWindow();
|
||||
|
||||
gfx::Rect rc = item->bounds();
|
||||
rc.y += rc.h - popup.border().bottom();
|
||||
|
||||
ui::fit_bounds(display(), &popup, gfx::Rect(rc.origin(), popup.sizeHint()));
|
||||
|
||||
popup.Open.connect([&popup] { popup.setHotRegion(gfx::Region(popup.boundsOnScreen())); });
|
||||
|
||||
popup.openWindowInForeground();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
Size,
|
||||
Style,
|
||||
Flags,
|
||||
Hinting,
|
||||
Popup,
|
||||
};
|
||||
|
||||
|
@ -40,6 +41,8 @@ public:
|
|||
obs::signal<void(const FontInfo&, From)> FontChange;
|
||||
|
||||
private:
|
||||
void onStyleItemClick(ButtonSet::Item* item);
|
||||
|
||||
class FontFace : public SearchEntry {
|
||||
public:
|
||||
FontFace();
|
||||
|
@ -73,17 +76,10 @@ private:
|
|||
FontStyle();
|
||||
};
|
||||
|
||||
class FontLigatures : public ButtonSet {
|
||||
public:
|
||||
FontLigatures();
|
||||
};
|
||||
|
||||
FontInfo m_info;
|
||||
FontFace m_face;
|
||||
FontSize m_size;
|
||||
FontStyle m_style;
|
||||
FontLigatures m_ligatures;
|
||||
ui::CheckBox m_antialias;
|
||||
bool m_lockFace = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -181,7 +181,8 @@ private:
|
|||
const FontInfo fontInfoDefSize(m_fontInfo,
|
||||
FontInfo::kDefaultSize,
|
||||
text::FontStyle(),
|
||||
FontInfo::Flags::Antialias);
|
||||
FontInfo::Flags::Antialias,
|
||||
text::FontHinting::Normal);
|
||||
const text::FontRef font = fonts->fontFromInfo(fontInfoDefSize);
|
||||
if (!font)
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue