Merge pull request #17578 from endragor/ft-outlines
Perfect FreeType-based outlines for DynamicFonts
This commit is contained in:
commit
9dbfe5dc61
13 changed files with 434 additions and 307 deletions
|
@ -87,6 +87,7 @@ void OutputStrings::_notification(int p_what) {
|
|||
float h_ofs = (int)h_scroll->get_value();
|
||||
Point2 icon_ofs = Point2(0, (font_height - (int)icon_error->get_height()) / 2);
|
||||
|
||||
FontDrawer drawer(font, Color(1, 1, 1));
|
||||
while (E && ofs.y < (size_height - (int)margin.y)) {
|
||||
|
||||
String str = E->get().text;
|
||||
|
|
|
@ -1121,6 +1121,7 @@ void ItemList::_notification(int p_what) {
|
|||
text_ofs += base_ofs;
|
||||
text_ofs += items[i].rect_cache.position;
|
||||
|
||||
FontDrawer drawer(font, Color(1, 1, 1));
|
||||
for (int j = 0; j < ss; j++) {
|
||||
|
||||
if (j == line_limit_cache[line]) {
|
||||
|
@ -1129,7 +1130,7 @@ void ItemList::_notification(int p_what) {
|
|||
if (line >= max_text_lines)
|
||||
break;
|
||||
}
|
||||
ofs += font->draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate);
|
||||
ofs += drawer.draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate);
|
||||
}
|
||||
|
||||
//special multiline mode
|
||||
|
|
|
@ -93,6 +93,7 @@ void Label::_notification(int p_what) {
|
|||
bool use_outline = get_constant("shadow_as_outline");
|
||||
Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
|
||||
int line_spacing = get_constant("line_spacing");
|
||||
Color font_outline_modulate = get_color("font_outline_modulate");
|
||||
|
||||
style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
||||
|
||||
|
@ -150,6 +151,7 @@ void Label::_notification(int p_what) {
|
|||
|
||||
int line = 0;
|
||||
int line_to = lines_skipped + (lines_visible > 0 ? lines_visible : 1);
|
||||
FontDrawer drawer(font, font_outline_modulate);
|
||||
while (wc) {
|
||||
/* handle lines not meant to be drawn quickly */
|
||||
if (line >= line_to)
|
||||
|
@ -244,11 +246,11 @@ void Label::_notification(int p_what) {
|
|||
n = String::char_uppercase(c);
|
||||
}
|
||||
|
||||
float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow);
|
||||
float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow, false);
|
||||
if (use_outline) {
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow);
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow, false);
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
|
||||
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
|
||||
}
|
||||
x_ofs_shadow += move;
|
||||
chars_total_shadow++;
|
||||
|
@ -265,7 +267,7 @@ void Label::_notification(int p_what) {
|
|||
n = String::char_uppercase(c);
|
||||
}
|
||||
|
||||
x_ofs += font->draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color);
|
||||
x_ofs += drawer.draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color);
|
||||
chars_total++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -656,6 +656,7 @@ void LineEdit::_notification(int p_what) {
|
|||
}
|
||||
|
||||
int caret_height = font->get_height() > y_area ? y_area : font->get_height();
|
||||
FontDrawer drawer(font, Color(1, 1, 1));
|
||||
while (true) {
|
||||
|
||||
//end of string, break!
|
||||
|
@ -683,7 +684,7 @@ void LineEdit::_notification(int p_what) {
|
|||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
|
||||
}
|
||||
|
||||
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||
drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||
|
||||
x_ofs += im_char_width;
|
||||
ofs++;
|
||||
|
@ -704,7 +705,7 @@ void LineEdit::_notification(int p_what) {
|
|||
if (selected)
|
||||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
|
||||
|
||||
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
|
||||
drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
|
||||
|
||||
if (char_ofs == cursor_pos && draw_caret) {
|
||||
if (ime_text.length() == 0) {
|
||||
|
@ -737,7 +738,7 @@ void LineEdit::_notification(int p_what) {
|
|||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
|
||||
}
|
||||
|
||||
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||
drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||
|
||||
x_ofs += im_char_width;
|
||||
ofs++;
|
||||
|
|
|
@ -286,6 +286,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|||
}
|
||||
|
||||
rchar = 0;
|
||||
FontDrawer drawer(font, Color(1, 1, 1));
|
||||
while (*c) {
|
||||
|
||||
int end = 0;
|
||||
|
@ -395,9 +396,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|||
}
|
||||
|
||||
if (selected) {
|
||||
font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
|
||||
drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
|
||||
} else {
|
||||
cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
|
||||
cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -782,6 +782,7 @@ void TextEdit::_notification(int p_what) {
|
|||
int line = cursor.line_ofs - 1;
|
||||
// another row may be visible during smooth scrolling
|
||||
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
|
||||
FontDrawer drawer(cache.font, Color(1, 1, 1));
|
||||
for (int i = 0; i < draw_amount; i++) {
|
||||
|
||||
line++;
|
||||
|
@ -1040,7 +1041,7 @@ void TextEdit::_notification(int p_what) {
|
|||
|
||||
if (brace_open_mismatch)
|
||||
color = cache.brace_mismatch_color;
|
||||
cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -1049,7 +1050,7 @@ void TextEdit::_notification(int p_what) {
|
|||
|
||||
if (brace_close_mismatch)
|
||||
color = cache.brace_mismatch_color;
|
||||
cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1082,7 +1083,7 @@ void TextEdit::_notification(int p_what) {
|
|||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
|
||||
}
|
||||
|
||||
cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
|
||||
drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
|
||||
|
||||
char_ofs += im_char_width;
|
||||
ofs++;
|
||||
|
@ -1109,7 +1110,7 @@ void TextEdit::_notification(int p_what) {
|
|||
}
|
||||
|
||||
if (str[j] >= 32) {
|
||||
int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
if (underlined) {
|
||||
draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
|
||||
}
|
||||
|
@ -1158,7 +1159,7 @@ void TextEdit::_notification(int p_what) {
|
|||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
|
||||
}
|
||||
|
||||
cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
|
||||
drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
|
||||
|
||||
char_ofs += im_char_width;
|
||||
ofs++;
|
||||
|
|
|
@ -415,6 +415,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
|||
|
||||
theme->set_color("font_color", "Label", Color(1, 1, 1));
|
||||
theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0));
|
||||
theme->set_color("font_outline_modulate", "Label", Color(1, 1, 1));
|
||||
|
||||
theme->set_constant("shadow_offset_x", "Label", 1 * scale);
|
||||
theme->set_constant("shadow_offset_y", "Label", 1 * scale);
|
||||
|
|
|
@ -33,8 +33,12 @@
|
|||
#include "os/file_access.h"
|
||||
#include "os/os.h"
|
||||
|
||||
bool DynamicFontData::CacheID::operator<(CacheID right) const {
|
||||
#include FT_STROKER_H
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
|
||||
bool DynamicFontData::CacheID::operator<(CacheID right) const {
|
||||
return key < right.key;
|
||||
}
|
||||
|
||||
|
@ -212,8 +216,8 @@ Error DynamicFontAtSize::_load() {
|
|||
error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
|
||||
}
|
||||
|
||||
ascent = (face->size->metrics.ascender >> 6) / oversampling * scale_color_font;
|
||||
descent = (-face->size->metrics.descender >> 6) / oversampling * scale_color_font;
|
||||
ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font;
|
||||
descent = (-face->size->metrics.descender / 64.0) / oversampling * scale_color_font;
|
||||
linegap = 0;
|
||||
texture_flags = 0;
|
||||
if (id.mipmaps)
|
||||
|
@ -241,18 +245,11 @@ float DynamicFontAtSize::get_descent() const {
|
|||
return descent;
|
||||
}
|
||||
|
||||
Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
|
||||
const Pair<const DynamicFontAtSize::Character *, DynamicFontAtSize *> DynamicFontAtSize::_find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
|
||||
const Character *chr = char_map.getptr(p_char);
|
||||
ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL)));
|
||||
|
||||
if (!valid)
|
||||
return Size2(1, 1);
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
|
||||
|
||||
const Character *c = char_map.getptr(p_char);
|
||||
ERR_FAIL_COND_V(!c, Size2());
|
||||
|
||||
Size2 ret(0, get_height());
|
||||
|
||||
if (!c->found) {
|
||||
if (!chr->found) {
|
||||
|
||||
//not found, try in fallbacks
|
||||
for (int i = 0; i < p_fallbacks.size(); i++) {
|
||||
|
@ -262,52 +259,53 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const V
|
|||
continue;
|
||||
|
||||
fb->_update_char(p_char);
|
||||
const Character *ch = fb->char_map.getptr(p_char);
|
||||
ERR_CONTINUE(!ch);
|
||||
const Character *fallback_chr = fb->char_map.getptr(p_char);
|
||||
ERR_CONTINUE(!fallback_chr);
|
||||
|
||||
if (!ch->found)
|
||||
if (!fallback_chr->found)
|
||||
continue;
|
||||
|
||||
c = ch;
|
||||
break;
|
||||
return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb);
|
||||
}
|
||||
|
||||
//not found, try 0xFFFD to display 'not found'.
|
||||
|
||||
if (!c->found) {
|
||||
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
|
||||
c = char_map.getptr(0xFFFD);
|
||||
ERR_FAIL_COND_V(!c, Size2());
|
||||
}
|
||||
chr = char_map.getptr(0xFFFD);
|
||||
ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL)));
|
||||
}
|
||||
|
||||
if (c->found) {
|
||||
ret.x = c->advance;
|
||||
}
|
||||
return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this));
|
||||
}
|
||||
|
||||
float DynamicFontAtSize::_get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const {
|
||||
float advance = 0.0;
|
||||
|
||||
if (p_next) {
|
||||
FT_Vector delta;
|
||||
FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
|
||||
|
||||
if (delta.x == 0) {
|
||||
for (int i = 0; i < p_fallbacks.size(); i++) {
|
||||
|
||||
DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
|
||||
if (!fb->valid)
|
||||
continue;
|
||||
|
||||
FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
|
||||
|
||||
if (delta.x == 0)
|
||||
continue;
|
||||
|
||||
ret.x += (delta.x >> 6) / oversampling;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret.x += (delta.x >> 6) / oversampling;
|
||||
FT_Get_Kerning(font->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
|
||||
advance = (delta.x / 64.0) / oversampling;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
|
||||
|
||||
if (!valid)
|
||||
return Size2(1, 1);
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
|
||||
|
||||
Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
|
||||
const Character *ch = char_pair_with_font.first;
|
||||
DynamicFontAtSize *font = char_pair_with_font.second;
|
||||
ERR_FAIL_COND_V(!ch, Size2());
|
||||
|
||||
Size2 ret(0, get_height());
|
||||
|
||||
if (ch->found) {
|
||||
ret.x = ch->advance;
|
||||
}
|
||||
ret.x += _get_kerning_advance(font, p_char, p_next);
|
||||
|
||||
// ensures oversampled glyphs will have enough space when this value is used by clipping/wrapping algorithms
|
||||
ret.x = Math::ceil(ret.x);
|
||||
|
@ -324,102 +322,42 @@ void DynamicFontAtSize::set_texture_flags(uint32_t p_flags) {
|
|||
}
|
||||
}
|
||||
|
||||
float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
|
||||
float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only) const {
|
||||
|
||||
if (!valid)
|
||||
return 0;
|
||||
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
|
||||
|
||||
const Character *c = char_map.getptr(p_char);
|
||||
Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
|
||||
const Character *ch = char_pair_with_font.first;
|
||||
DynamicFontAtSize *font = char_pair_with_font.second;
|
||||
|
||||
float advance = 0;
|
||||
ERR_FAIL_COND_V(!ch, 0.0);
|
||||
|
||||
if (!c->found) {
|
||||
float advance = 0.0;
|
||||
|
||||
//not found, try in fallbacks
|
||||
bool used_fallback = false;
|
||||
if (ch->found) {
|
||||
ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0);
|
||||
|
||||
for (int i = 0; i < p_fallbacks.size(); i++) {
|
||||
|
||||
DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
|
||||
if (!fb->valid)
|
||||
continue;
|
||||
|
||||
fb->_update_char(p_char);
|
||||
const Character *ch = fb->char_map.getptr(p_char);
|
||||
ERR_CONTINUE(!ch);
|
||||
|
||||
if (!ch->found)
|
||||
continue;
|
||||
if (!p_advance_only && ch->texture_idx != -1) {
|
||||
Point2 cpos = p_pos;
|
||||
cpos.x += ch->h_align;
|
||||
cpos.y -= fb->get_ascent();
|
||||
cpos.y -= font->get_ascent();
|
||||
cpos.y += ch->v_align;
|
||||
ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= fb->textures.size(), 0);
|
||||
if (ch->texture_idx != -1) {
|
||||
Color modulate = p_modulate;
|
||||
if (FT_HAS_COLOR(fb->face)) {
|
||||
modulate.r = modulate.g = modulate.b = 1;
|
||||
}
|
||||
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(fb->scale_color_font, fb->scale_color_font)), fb->textures[ch->texture_idx].texture->get_rid(), ch->rect_uv, modulate, false, RID(), false);
|
||||
}
|
||||
advance = ch->advance;
|
||||
used_fallback = true;
|
||||
break;
|
||||
}
|
||||
//not found, try 0xFFFD to display 'not found'.
|
||||
|
||||
if (!used_fallback) {
|
||||
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
|
||||
c = char_map.getptr(0xFFFD);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->found) {
|
||||
|
||||
Point2 cpos = p_pos;
|
||||
cpos.x += c->h_align;
|
||||
cpos.y -= get_ascent();
|
||||
cpos.y += c->v_align;
|
||||
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
|
||||
if (c->texture_idx != -1) {
|
||||
Color modulate = p_modulate;
|
||||
if (FT_HAS_COLOR(face)) {
|
||||
modulate.r = modulate.g = modulate.b = 1;
|
||||
modulate.r = modulate.g = modulate.b = 1.0;
|
||||
}
|
||||
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size * Vector2(scale_color_font, scale_color_font)), textures[c->texture_idx].texture->get_rid(), c->rect_uv, modulate, false, RID(), false);
|
||||
}
|
||||
advance = c->advance;
|
||||
//textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
|
||||
RID texture = font->textures[ch->texture_idx].texture->get_rid();
|
||||
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(font->scale_color_font, font->scale_color_font)), texture, ch->rect_uv, modulate, false, RID(), false);
|
||||
}
|
||||
|
||||
if (p_next) {
|
||||
|
||||
FT_Vector delta;
|
||||
FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
|
||||
|
||||
if (delta.x == 0) {
|
||||
for (int i = 0; i < p_fallbacks.size(); i++) {
|
||||
|
||||
DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
|
||||
if (!fb->valid)
|
||||
continue;
|
||||
|
||||
FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
|
||||
|
||||
if (delta.x == 0)
|
||||
continue;
|
||||
|
||||
advance += (delta.x >> 6) / oversampling;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
advance += (delta.x >> 6) / oversampling;
|
||||
}
|
||||
advance = ch->advance;
|
||||
}
|
||||
|
||||
advance += _get_kerning_advance(font, p_char, p_next);
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
|
@ -443,96 +381,37 @@ void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) {
|
|||
memdelete(f);
|
||||
}
|
||||
|
||||
void DynamicFontAtSize::_update_char(CharType p_char) {
|
||||
|
||||
if (char_map.has(p_char))
|
||||
return;
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
|
||||
if (FT_Get_Char_Index(face, p_char) == 0) {
|
||||
//not found
|
||||
DynamicFontAtSize::Character DynamicFontAtSize::Character::not_found() {
|
||||
Character ch;
|
||||
ch.texture_idx = -1;
|
||||
ch.advance = 0;
|
||||
ch.h_align = 0;
|
||||
ch.v_align = 0;
|
||||
ch.found = false;
|
||||
return ch;
|
||||
}
|
||||
|
||||
char_map[p_char] = ch;
|
||||
return;
|
||||
}
|
||||
DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
|
||||
TexturePosition ret;
|
||||
ret.index = -1;
|
||||
ret.x = 0;
|
||||
ret.y = 0;
|
||||
|
||||
int ft_hinting;
|
||||
|
||||
switch (font->hinting) {
|
||||
case DynamicFontData::HINTING_NONE:
|
||||
ft_hinting = FT_LOAD_NO_HINTING;
|
||||
break;
|
||||
case DynamicFontData::HINTING_LIGHT:
|
||||
ft_hinting = FT_LOAD_TARGET_LIGHT;
|
||||
break;
|
||||
default:
|
||||
ft_hinting = FT_LOAD_TARGET_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
|
||||
if (!error) {
|
||||
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
}
|
||||
if (error) {
|
||||
|
||||
int advance = 0;
|
||||
Character ch;
|
||||
ch.texture_idx = -1;
|
||||
ch.advance = advance;
|
||||
ch.h_align = 0;
|
||||
ch.v_align = 0;
|
||||
ch.found = false;
|
||||
|
||||
char_map[p_char] = ch;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int w = slot->bitmap.width;
|
||||
int h = slot->bitmap.rows;
|
||||
int yofs = slot->bitmap_top;
|
||||
int xofs = slot->bitmap_left;
|
||||
int advance = slot->advance.x >> 6;
|
||||
|
||||
int mw = w + rect_margin * 2;
|
||||
int mh = h + rect_margin * 2;
|
||||
|
||||
if (mw > 4096 || mh > 4096) {
|
||||
|
||||
ERR_FAIL_COND(mw > 4096);
|
||||
ERR_FAIL_COND(mh > 4096);
|
||||
}
|
||||
|
||||
//find a texture to fit this...
|
||||
|
||||
int color_size = slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
|
||||
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
|
||||
int tex_index = -1;
|
||||
int tex_x = 0;
|
||||
int tex_y = 0;
|
||||
int mw = p_width;
|
||||
int mh = p_height;
|
||||
|
||||
for (int i = 0; i < textures.size(); i++) {
|
||||
|
||||
CharTexture &ct = textures[i];
|
||||
|
||||
if (ct.texture->get_format() != require_format)
|
||||
if (ct.texture->get_format() != p_image_format)
|
||||
continue;
|
||||
|
||||
if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
|
||||
continue;
|
||||
|
||||
tex_y = 0x7FFFFFFF;
|
||||
tex_x = 0;
|
||||
ret.y = 0x7FFFFFFF;
|
||||
ret.x = 0;
|
||||
|
||||
for (int j = 0; j < ct.texture_size - mw; j++) {
|
||||
|
||||
|
@ -545,25 +424,27 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
|
|||
max_y = y;
|
||||
}
|
||||
|
||||
if (max_y < tex_y) {
|
||||
tex_y = max_y;
|
||||
tex_x = j;
|
||||
if (max_y < ret.y) {
|
||||
ret.y = max_y;
|
||||
ret.x = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (tex_y == 0x7FFFFFFF || tex_y + mh > ct.texture_size)
|
||||
if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size)
|
||||
continue; //fail, could not fit it here
|
||||
|
||||
tex_index = i;
|
||||
ret.index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tex_index == -1) {
|
||||
//could not find texture to fit, create one
|
||||
tex_x = 0;
|
||||
tex_y = 0;
|
||||
//print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));
|
||||
|
||||
int texsize = MAX(id.size * 8, 256);
|
||||
if (ret.index == -1) {
|
||||
//could not find texture to fit, create one
|
||||
ret.x = 0;
|
||||
ret.y = 0;
|
||||
|
||||
int texsize = MAX(id.size * oversampling * 8, 256);
|
||||
if (mw > texsize)
|
||||
texsize = mw; //special case, adapt to it?
|
||||
if (mh > texsize)
|
||||
|
@ -575,13 +456,13 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
|
|||
|
||||
CharTexture tex;
|
||||
tex.texture_size = texsize;
|
||||
tex.imgdata.resize(texsize * texsize * color_size);
|
||||
tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
|
||||
|
||||
{
|
||||
//zero texture
|
||||
PoolVector<uint8_t>::Write w = tex.imgdata.write();
|
||||
ERR_FAIL_COND(texsize * texsize * color_size > tex.imgdata.size());
|
||||
for (int i = 0; i < texsize * texsize * color_size; i++) {
|
||||
ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
|
||||
for (int i = 0; i < texsize * texsize * p_color_size; i++) {
|
||||
w[i] = 0;
|
||||
}
|
||||
}
|
||||
|
@ -590,12 +471,31 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
|
|||
tex.offsets[i] = 0;
|
||||
|
||||
textures.push_back(tex);
|
||||
tex_index = textures.size() - 1;
|
||||
ret.index = textures.size() - 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance) {
|
||||
int w = bitmap.width;
|
||||
int h = bitmap.rows;
|
||||
|
||||
int mw = w + rect_margin * 2;
|
||||
int mh = h + rect_margin * 2;
|
||||
|
||||
ERR_FAIL_COND_V(mw > 4096, Character::not_found());
|
||||
ERR_FAIL_COND_V(mh > 4096, Character::not_found());
|
||||
|
||||
int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
|
||||
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
|
||||
|
||||
TexturePosition tex_pos = _find_texture_pos_for_glyph(color_size, require_format, mw, mh);
|
||||
ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
|
||||
|
||||
//fit character in char texture
|
||||
|
||||
CharTexture &tex = textures[tex_index];
|
||||
CharTexture &tex = textures[tex_pos.index];
|
||||
|
||||
{
|
||||
PoolVector<uint8_t>::Write wr = tex.imgdata.write();
|
||||
|
@ -603,30 +503,30 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
|
|||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
|
||||
int ofs = ((i + tex_y + rect_margin) * tex.texture_size + j + tex_x + rect_margin) * color_size;
|
||||
ERR_FAIL_COND(ofs >= tex.imgdata.size());
|
||||
switch (slot->bitmap.pixel_mode) {
|
||||
int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
|
||||
ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
|
||||
switch (bitmap.pixel_mode) {
|
||||
case FT_PIXEL_MODE_MONO: {
|
||||
int byte = i * slot->bitmap.pitch + (j >> 3);
|
||||
int byte = i * bitmap.pitch + (j >> 3);
|
||||
int bit = 1 << (7 - (j % 8));
|
||||
wr[ofs + 0] = 255; //grayscale as 1
|
||||
wr[ofs + 1] = slot->bitmap.buffer[byte] & bit ? 255 : 0;
|
||||
wr[ofs + 1] = bitmap.buffer[byte] & bit ? 255 : 0;
|
||||
} break;
|
||||
case FT_PIXEL_MODE_GRAY:
|
||||
wr[ofs + 0] = 255; //grayscale as 1
|
||||
wr[ofs + 1] = slot->bitmap.buffer[i * slot->bitmap.pitch + j];
|
||||
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
|
||||
break;
|
||||
case FT_PIXEL_MODE_BGRA: {
|
||||
int ofs_color = i * slot->bitmap.pitch + (j << 2);
|
||||
wr[ofs + 2] = slot->bitmap.buffer[ofs_color + 0];
|
||||
wr[ofs + 1] = slot->bitmap.buffer[ofs_color + 1];
|
||||
wr[ofs + 0] = slot->bitmap.buffer[ofs_color + 2];
|
||||
wr[ofs + 3] = slot->bitmap.buffer[ofs_color + 3];
|
||||
int ofs_color = i * bitmap.pitch + (j << 2);
|
||||
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
|
||||
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
|
||||
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
|
||||
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
|
||||
} break;
|
||||
// TODO: FT_PIXEL_MODE_LCD
|
||||
default:
|
||||
ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(slot->bitmap.pixel_mode));
|
||||
ERR_FAIL();
|
||||
ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(bitmap.pixel_mode));
|
||||
ERR_FAIL_V(Character::not_found());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -648,31 +548,105 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
|
|||
|
||||
// update height array
|
||||
|
||||
for (int k = tex_x; k < tex_x + mw; k++) {
|
||||
|
||||
tex.offsets[k] = tex_y + mh;
|
||||
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
|
||||
tex.offsets[k] = tex_pos.y + mh;
|
||||
}
|
||||
|
||||
Character chr;
|
||||
chr.h_align = xofs * scale_color_font / oversampling;
|
||||
chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent;
|
||||
chr.advance = advance * scale_color_font / oversampling;
|
||||
chr.texture_idx = tex_index;
|
||||
chr.texture_idx = tex_pos.index;
|
||||
chr.found = true;
|
||||
|
||||
chr.rect_uv = Rect2(tex_x + rect_margin, tex_y + rect_margin, w, h);
|
||||
chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
|
||||
chr.rect = chr.rect_uv;
|
||||
chr.rect.position /= oversampling;
|
||||
chr.rect.size /= oversampling;
|
||||
|
||||
char_map[p_char] = chr;
|
||||
chr.rect.size = chr.rect.size * scale_color_font / oversampling;
|
||||
return chr;
|
||||
}
|
||||
|
||||
bool DynamicFontAtSize::update_oversampling() {
|
||||
if (oversampling == font_oversampling)
|
||||
return false;
|
||||
if (!valid)
|
||||
return false;
|
||||
DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_char) {
|
||||
Character ret = Character::not_found();
|
||||
|
||||
if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0)
|
||||
return ret;
|
||||
|
||||
FT_Stroker stroker;
|
||||
if (FT_Stroker_New(library, &stroker) != 0)
|
||||
return ret;
|
||||
|
||||
FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
|
||||
FT_Glyph glyph;
|
||||
FT_BitmapGlyph glyph_bitmap;
|
||||
|
||||
if (FT_Get_Glyph(face->glyph, &glyph) != 0)
|
||||
goto cleanup_stroker;
|
||||
if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0)
|
||||
goto cleanup_glyph;
|
||||
if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1) != 0)
|
||||
goto cleanup_glyph;
|
||||
|
||||
glyph_bitmap = (FT_BitmapGlyph)glyph;
|
||||
ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0);
|
||||
|
||||
cleanup_glyph:
|
||||
FT_Done_Glyph(glyph);
|
||||
cleanup_stroker:
|
||||
FT_Stroker_Done(stroker);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DynamicFontAtSize::_update_char(CharType p_char) {
|
||||
|
||||
if (char_map.has(p_char))
|
||||
return;
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
Character character = Character::not_found();
|
||||
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
|
||||
if (FT_Get_Char_Index(face, p_char) == 0) {
|
||||
char_map[p_char] = character;
|
||||
return;
|
||||
}
|
||||
|
||||
int ft_hinting;
|
||||
|
||||
switch (font->hinting) {
|
||||
case DynamicFontData::HINTING_NONE:
|
||||
ft_hinting = FT_LOAD_NO_HINTING;
|
||||
break;
|
||||
case DynamicFontData::HINTING_LIGHT:
|
||||
ft_hinting = FT_LOAD_TARGET_LIGHT;
|
||||
break;
|
||||
default:
|
||||
ft_hinting = FT_LOAD_TARGET_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
|
||||
if (error) {
|
||||
char_map[p_char] = character;
|
||||
return;
|
||||
}
|
||||
|
||||
if (id.outline_size > 0) {
|
||||
character = _make_outline_char(p_char);
|
||||
} else {
|
||||
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (!error)
|
||||
character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
|
||||
}
|
||||
|
||||
char_map[p_char] = character;
|
||||
}
|
||||
|
||||
void DynamicFontAtSize::update_oversampling() {
|
||||
if (oversampling == font_oversampling || !valid)
|
||||
return;
|
||||
|
||||
FT_Done_FreeType(library);
|
||||
textures.clear();
|
||||
|
@ -680,8 +654,6 @@ bool DynamicFontAtSize::update_oversampling() {
|
|||
oversampling = font_oversampling;
|
||||
valid = false;
|
||||
_load();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DynamicFontAtSize::DynamicFontAtSize() {
|
||||
|
@ -710,11 +682,27 @@ DynamicFontAtSize::~DynamicFontAtSize() {
|
|||
void DynamicFont::_reload_cache() {
|
||||
|
||||
ERR_FAIL_COND(cache_id.size < 1);
|
||||
if (!data.is_valid())
|
||||
if (!data.is_valid()) {
|
||||
data_at_size.unref();
|
||||
outline_data_at_size.unref();
|
||||
fallback_data_at_size.resize(0);
|
||||
fallback_outline_data_at_size.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
data_at_size = data->_get_dynamic_font_at_size(cache_id);
|
||||
if (outline_cache_id.outline_size > 0) {
|
||||
outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id);
|
||||
fallback_outline_data_at_size.resize(fallback_data_at_size.size());
|
||||
} else {
|
||||
outline_data_at_size.unref();
|
||||
fallback_outline_data_at_size.resize(0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fallbacks.size(); i++) {
|
||||
fallback_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(cache_id);
|
||||
if (outline_cache_id.outline_size > 0)
|
||||
fallback_outline_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(outline_cache_id);
|
||||
}
|
||||
|
||||
emit_changed();
|
||||
|
@ -724,12 +712,10 @@ void DynamicFont::_reload_cache() {
|
|||
void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) {
|
||||
|
||||
data = p_data;
|
||||
if (data.is_valid())
|
||||
data_at_size = data->_get_dynamic_font_at_size(cache_id);
|
||||
else
|
||||
data_at_size = Ref<DynamicFontAtSize>();
|
||||
_reload_cache();
|
||||
|
||||
emit_changed();
|
||||
_change_notify();
|
||||
}
|
||||
|
||||
Ref<DynamicFontData> DynamicFont::get_font_data() const {
|
||||
|
@ -742,6 +728,7 @@ void DynamicFont::set_size(int p_size) {
|
|||
if (cache_id.size == p_size)
|
||||
return;
|
||||
cache_id.size = p_size;
|
||||
outline_cache_id.size = p_size;
|
||||
_reload_cache();
|
||||
}
|
||||
|
||||
|
@ -750,6 +737,30 @@ int DynamicFont::get_size() const {
|
|||
return cache_id.size;
|
||||
}
|
||||
|
||||
void DynamicFont::set_outline_size(int p_size) {
|
||||
if (outline_cache_id.outline_size == p_size)
|
||||
return;
|
||||
ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX);
|
||||
outline_cache_id.outline_size = p_size;
|
||||
_reload_cache();
|
||||
}
|
||||
|
||||
int DynamicFont::get_outline_size() const {
|
||||
return outline_cache_id.outline_size;
|
||||
}
|
||||
|
||||
void DynamicFont::set_outline_color(Color p_color) {
|
||||
if (p_color != outline_color) {
|
||||
outline_color = p_color;
|
||||
emit_changed();
|
||||
_change_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Color DynamicFont::get_outline_color() const {
|
||||
return outline_color;
|
||||
}
|
||||
|
||||
bool DynamicFont::get_use_mipmaps() const {
|
||||
|
||||
return cache_id.mipmaps;
|
||||
|
@ -760,6 +771,7 @@ void DynamicFont::set_use_mipmaps(bool p_enable) {
|
|||
if (cache_id.mipmaps == p_enable)
|
||||
return;
|
||||
cache_id.mipmaps = p_enable;
|
||||
outline_cache_id.mipmaps = p_enable;
|
||||
_reload_cache();
|
||||
}
|
||||
|
||||
|
@ -773,6 +785,7 @@ void DynamicFont::set_use_filter(bool p_enable) {
|
|||
if (cache_id.filter == p_enable)
|
||||
return;
|
||||
cache_id.filter = p_enable;
|
||||
outline_cache_id.filter = p_enable;
|
||||
_reload_cache();
|
||||
}
|
||||
|
||||
|
@ -862,13 +875,24 @@ bool DynamicFont::is_distance_field_hint() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
|
||||
bool DynamicFont::has_outline() const {
|
||||
return outline_cache_id.outline_size > 0;
|
||||
}
|
||||
|
||||
if (!data_at_size.is_valid())
|
||||
float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
|
||||
const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size;
|
||||
|
||||
if (!font_at_size.is_valid())
|
||||
return 0;
|
||||
|
||||
return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, fallback_data_at_size) + spacing_char;
|
||||
const Vector<Ref<DynamicFontAtSize> > &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size;
|
||||
Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate;
|
||||
|
||||
// If requested outline draw, but no outline is present, simply return advance without drawing anything
|
||||
bool advance_only = p_outline && outline_cache_id.outline_size == 0;
|
||||
return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only) + spacing_char;
|
||||
}
|
||||
|
||||
void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) {
|
||||
|
||||
ERR_FAIL_COND(p_data.is_null());
|
||||
|
@ -882,6 +906,8 @@ void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) {
|
|||
ERR_FAIL_COND(p_data.is_null());
|
||||
fallbacks.push_back(p_data);
|
||||
fallback_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const..
|
||||
if (outline_cache_id.outline_size > 0)
|
||||
fallback_outline_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id));
|
||||
|
||||
_change_notify();
|
||||
emit_changed();
|
||||
|
@ -966,6 +992,12 @@ void DynamicFont::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size);
|
||||
ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size);
|
||||
ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color);
|
||||
ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_mipmaps", "enable"), &DynamicFont::set_use_mipmaps);
|
||||
ClassDB::bind_method(D_METHOD("get_use_mipmaps"), &DynamicFont::get_use_mipmaps);
|
||||
ClassDB::bind_method(D_METHOD("set_use_filter", "enable"), &DynamicFont::set_use_filter);
|
||||
|
@ -981,6 +1013,8 @@ void DynamicFont::_bind_methods() {
|
|||
|
||||
ADD_GROUP("Settings", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "size"), "set_size", "get_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size"), "set_outline_size", "get_outline_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "get_use_mipmaps");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_filter"), "set_use_filter", "get_use_filter");
|
||||
ADD_GROUP("Extra Spacing", "extra_spacing");
|
||||
|
@ -1008,6 +1042,7 @@ DynamicFont::DynamicFont() :
|
|||
spacing_bottom = 0;
|
||||
spacing_char = 0;
|
||||
spacing_space = 0;
|
||||
outline_color = Color(1, 1, 1);
|
||||
if (dynamic_font_mutex)
|
||||
dynamic_font_mutex->lock();
|
||||
dynamic_fonts.add(&font_list);
|
||||
|
@ -1043,7 +1078,8 @@ void DynamicFont::update_oversampling() {
|
|||
SelfList<DynamicFont> *E = dynamic_fonts.first();
|
||||
while (E) {
|
||||
|
||||
if (E->self()->data_at_size.is_valid() && E->self()->data_at_size->update_oversampling()) {
|
||||
if (E->self()->data_at_size.is_valid()) {
|
||||
E->self()->data_at_size->update_oversampling();
|
||||
changed.push_back(Ref<DynamicFont>(E->self()));
|
||||
}
|
||||
E = E->next();
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "io/resource_loader.h"
|
||||
#include "os/mutex.h"
|
||||
#include "os/thread_safe.h"
|
||||
#include "pair.h"
|
||||
#include "scene/resources/font.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
|
@ -49,10 +50,10 @@ class DynamicFontData : public Resource {
|
|||
|
||||
public:
|
||||
struct CacheID {
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t size : 16;
|
||||
uint32_t outline_size : 8;
|
||||
bool mipmaps : 1;
|
||||
bool filter : 1;
|
||||
};
|
||||
|
@ -148,8 +149,22 @@ class DynamicFontAtSize : public Reference {
|
|||
texture_idx = 0;
|
||||
v_align = 0;
|
||||
}
|
||||
|
||||
static Character not_found();
|
||||
};
|
||||
|
||||
struct TexturePosition {
|
||||
int index;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
const Pair<const Character *, DynamicFontAtSize *> _find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
|
||||
Character _make_outline_char(CharType p_char);
|
||||
float _get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const;
|
||||
TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height);
|
||||
Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance);
|
||||
|
||||
static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count);
|
||||
static void _ft_stream_close(FT_Stream stream);
|
||||
|
||||
|
@ -174,10 +189,10 @@ public:
|
|||
|
||||
Size2 get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
|
||||
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only = false) const;
|
||||
|
||||
void set_texture_flags(uint32_t p_flags);
|
||||
bool update_oversampling();
|
||||
void update_oversampling();
|
||||
|
||||
DynamicFontAtSize();
|
||||
~DynamicFontAtSize();
|
||||
|
@ -200,17 +215,23 @@ public:
|
|||
private:
|
||||
Ref<DynamicFontData> data;
|
||||
Ref<DynamicFontAtSize> data_at_size;
|
||||
Ref<DynamicFontAtSize> outline_data_at_size;
|
||||
|
||||
Vector<Ref<DynamicFontData> > fallbacks;
|
||||
Vector<Ref<DynamicFontAtSize> > fallback_data_at_size;
|
||||
Vector<Ref<DynamicFontAtSize> > fallback_outline_data_at_size;
|
||||
|
||||
DynamicFontData::CacheID cache_id;
|
||||
DynamicFontData::CacheID outline_cache_id;
|
||||
|
||||
bool valid;
|
||||
int spacing_top;
|
||||
int spacing_bottom;
|
||||
int spacing_char;
|
||||
int spacing_space;
|
||||
|
||||
Color outline_color;
|
||||
|
||||
protected:
|
||||
void _reload_cache();
|
||||
|
||||
|
@ -227,6 +248,12 @@ public:
|
|||
void set_size(int p_size);
|
||||
int get_size() const;
|
||||
|
||||
void set_outline_size(int p_size);
|
||||
int get_outline_size() const;
|
||||
|
||||
void set_outline_color(Color p_color);
|
||||
Color get_outline_color() const;
|
||||
|
||||
bool get_use_mipmaps() const;
|
||||
void set_use_mipmaps(bool p_enable);
|
||||
|
||||
|
@ -251,7 +278,9 @@ public:
|
|||
|
||||
virtual bool is_distance_field_hint() const;
|
||||
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
|
||||
virtual bool has_outline() const;
|
||||
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
|
||||
|
||||
SelfList<DynamicFont> font_list;
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next) const {
|
|||
return ret;
|
||||
}
|
||||
|
||||
float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
|
||||
float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
|
||||
|
||||
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
|
||||
|
||||
|
@ -172,6 +172,7 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!p_outline) {
|
||||
Point2 cpos = p_pos;
|
||||
cpos.x += c->h_align;
|
||||
cpos.y -= get_ascent();
|
||||
|
@ -179,6 +180,7 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT
|
|||
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
|
||||
if (c->texture_idx != -1)
|
||||
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect, p_modulate);
|
||||
}
|
||||
|
||||
//textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
|
||||
|
||||
|
@ -459,12 +461,12 @@ bool DynamicFont::is_distance_field_hint() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
|
||||
float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
|
||||
|
||||
if (!data_at_size.is_valid())
|
||||
return 0;
|
||||
|
||||
return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate);
|
||||
return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
|
||||
}
|
||||
|
||||
DynamicFont::DynamicFont() {
|
||||
|
|
|
@ -136,7 +136,7 @@ public:
|
|||
|
||||
Size2 get_char_size(CharType p_char, CharType p_next = 0) const;
|
||||
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
|
||||
|
||||
DynamicFontAtSize();
|
||||
~DynamicFontAtSize();
|
||||
|
@ -171,7 +171,7 @@ public:
|
|||
|
||||
virtual bool is_distance_field_hint() const;
|
||||
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
|
||||
|
||||
DynamicFont();
|
||||
~DynamicFont();
|
||||
|
|
|
@ -32,12 +32,12 @@
|
|||
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "method_bind_ext.gen.inc"
|
||||
|
||||
void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate) const {
|
||||
|
||||
void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const {
|
||||
float length = get_string_size(p_text).width;
|
||||
if (length >= p_width) {
|
||||
draw(p_canvas_item, p_pos, p_text, p_modulate, p_width);
|
||||
draw(p_canvas_item, p_pos, p_text, p_modulate, p_width, p_outline_modulate);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -56,13 +56,14 @@ void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, f
|
|||
ERR_PRINT("Unknown halignment type");
|
||||
} break;
|
||||
}
|
||||
draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width);
|
||||
draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate);
|
||||
}
|
||||
|
||||
void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) const {
|
||||
|
||||
void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const {
|
||||
Vector2 ofs;
|
||||
|
||||
int chars_drawn = 0;
|
||||
bool with_outline = has_outline();
|
||||
for (int i = 0; i < p_text.length(); i++) {
|
||||
|
||||
int width = get_char_size(p_text[i]).width;
|
||||
|
@ -70,7 +71,15 @@ void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, co
|
|||
if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w)
|
||||
break; //clip
|
||||
|
||||
ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate);
|
||||
ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline);
|
||||
++chars_drawn;
|
||||
}
|
||||
|
||||
if (has_outline()) {
|
||||
ofs = Vector2(0, 0);
|
||||
for (int i = 0; i < chars_drawn; i++) {
|
||||
ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,13 +90,14 @@ void Font::update_changes() {
|
|||
|
||||
void Font::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
|
||||
ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent);
|
||||
ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent);
|
||||
ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
|
||||
ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
|
||||
ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
|
||||
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
|
||||
ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
|
||||
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
|
||||
}
|
||||
|
||||
|
@ -494,23 +504,24 @@ Ref<BitmapFont> BitmapFont::get_fallback() const {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
|
||||
float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
|
||||
|
||||
const Character *c = char_map.getptr(p_char);
|
||||
|
||||
if (!c) {
|
||||
if (fallback.is_valid())
|
||||
return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate);
|
||||
return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
|
||||
if (!p_outline && c->texture_idx != -1) {
|
||||
Point2 cpos = p_pos;
|
||||
cpos.x += c->h_align;
|
||||
cpos.y -= ascent;
|
||||
cpos.y += c->v_align;
|
||||
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
|
||||
if (c->texture_idx != -1)
|
||||
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, RID(), false);
|
||||
}
|
||||
|
||||
return get_char_size(p_char, p_next).width;
|
||||
}
|
||||
|
|
|
@ -56,14 +56,55 @@ public:
|
|||
|
||||
virtual bool is_distance_field_hint() const = 0;
|
||||
|
||||
void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1) const;
|
||||
void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1)) const;
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const = 0;
|
||||
void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const;
|
||||
void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const;
|
||||
|
||||
virtual bool has_outline() const { return false; }
|
||||
virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0;
|
||||
|
||||
void update_changes();
|
||||
Font();
|
||||
};
|
||||
|
||||
// Helper class to that draws outlines immediately and draws characters in its destructor.
|
||||
class FontDrawer {
|
||||
const Ref<Font> &font;
|
||||
Color outline_color;
|
||||
bool has_outline;
|
||||
|
||||
struct PendingDraw {
|
||||
RID canvas_item;
|
||||
Point2 pos;
|
||||
CharType chr;
|
||||
CharType next;
|
||||
Color modulate;
|
||||
};
|
||||
|
||||
Vector<PendingDraw> pending_draws;
|
||||
|
||||
public:
|
||||
FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) :
|
||||
font(p_font),
|
||||
outline_color(p_outline_color) {
|
||||
has_outline = p_font->has_outline();
|
||||
}
|
||||
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) {
|
||||
if (has_outline) {
|
||||
PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate };
|
||||
pending_draws.push_back(draw);
|
||||
}
|
||||
return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline);
|
||||
}
|
||||
|
||||
~FontDrawer() {
|
||||
for (int i = 0; i < pending_draws.size(); ++i) {
|
||||
const PendingDraw &draw = pending_draws[i];
|
||||
font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BitmapFont : public Font {
|
||||
|
||||
GDCLASS(BitmapFont, Font);
|
||||
|
@ -153,7 +194,7 @@ public:
|
|||
void set_distance_field_hint(bool p_distance_field);
|
||||
bool is_distance_field_hint() const;
|
||||
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
|
||||
float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
|
||||
|
||||
BitmapFont();
|
||||
~BitmapFont();
|
||||
|
|
Loading…
Reference in a new issue