mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:27:35 +00:00
FontEditor: Update editor to handle new font format
The editor now unmasks fonts on load, mapping their glyphs to the complete unicode character set, and masks them upon saving to reduce disk space. This is a naive approach in terms of memory usage and can be improved but whose immediate goal is to allow editing any glyph without concern for range allocation.
This commit is contained in:
parent
9bcfdfc03b
commit
a486415f03
10 changed files with 26 additions and 91 deletions
|
@ -119,7 +119,6 @@ FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&
|
|||
m_family_textbox = *find_descendant_of_type_named<GUI::TextBox>("family_textbox");
|
||||
m_presentation_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("presentation_spinbox");
|
||||
m_weight_combobox = *find_descendant_of_type_named<GUI::ComboBox>("weight_combobox");
|
||||
m_type_combobox = *find_descendant_of_type_named<GUI::ComboBox>("type_combobox");
|
||||
m_spacing_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("spacing_spinbox");
|
||||
m_mean_line_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("mean_line_spinbox");
|
||||
m_baseline_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("baseline_spinbox");
|
||||
|
@ -170,7 +169,7 @@ FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&
|
|||
if (new_font_wizard->exec() == GUI::Dialog::ExecOK) {
|
||||
auto metadata = new_font_wizard->new_font_metadata();
|
||||
|
||||
RefPtr<Gfx::BitmapFont> new_font = Gfx::BitmapFont::create(metadata.glyph_height, metadata.glyph_width, metadata.is_fixed_width, metadata.type);
|
||||
RefPtr<Gfx::BitmapFont> new_font = Gfx::BitmapFont::create(metadata.glyph_height, metadata.glyph_width, metadata.is_fixed_width, 0x110000);
|
||||
if (!new_font) {
|
||||
GUI::MessageBox::show(window(), "Failed to create new font.", "Font Editor", GUI::MessageBox::Type::Error);
|
||||
return;
|
||||
|
@ -208,7 +207,7 @@ FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&
|
|||
GUI::MessageBox::show(window(), message, "Font Editor", GUI::MessageBox::Type::Error);
|
||||
return;
|
||||
}
|
||||
RefPtr<Gfx::BitmapFont> new_font = static_ptr_cast<Gfx::BitmapFont>(bitmap_font->clone());
|
||||
RefPtr<Gfx::BitmapFont> new_font = static_ptr_cast<Gfx::BitmapFont>(bitmap_font->unmasked_character_set());
|
||||
if (!new_font) {
|
||||
String message = String::formatted("Couldn't load font: {}\n", open_path.value());
|
||||
GUI::MessageBox::show(window(), message, "Font Editor", GUI::MessageBox::Type::Error);
|
||||
|
@ -396,12 +395,6 @@ FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&
|
|||
did_modify_font();
|
||||
};
|
||||
|
||||
m_type_combobox->on_change = [this](auto&, const auto& index) {
|
||||
m_edited_font->set_type(static_cast<Gfx::FontTypes>(index.row()));
|
||||
m_glyph_map_widget->reprobe_font();
|
||||
did_modify_font();
|
||||
};
|
||||
|
||||
m_presentation_spinbox->on_change = [this, update_demo](int value) {
|
||||
m_edited_font->set_presentation_size(value);
|
||||
update_demo();
|
||||
|
@ -487,16 +480,6 @@ void FontEditorWidget::initialize(const String& path, RefPtr<Gfx::BitmapFont>&&
|
|||
i++;
|
||||
}
|
||||
|
||||
m_font_type_list.clear();
|
||||
StringBuilder type_count;
|
||||
for (int i = 0; i < Gfx::FontTypes::__Count; i++) {
|
||||
type_count.appendff("{}", Gfx::BitmapFont::type_name_by_type(static_cast<Gfx::FontTypes>(i)));
|
||||
m_font_type_list.append(type_count.to_string());
|
||||
type_count.clear();
|
||||
}
|
||||
m_type_combobox->set_model(*GUI::ItemListModel<String>::create(m_font_type_list));
|
||||
m_type_combobox->set_selected_index(m_edited_font->type());
|
||||
|
||||
m_fixed_width_checkbox->set_checked(m_edited_font->is_fixed_width());
|
||||
|
||||
m_glyph_map_widget->set_selected_glyph('A');
|
||||
|
@ -562,7 +545,8 @@ void FontEditorWidget::initialize_menubar(GUI::Window& window)
|
|||
|
||||
bool FontEditorWidget::save_as(const String& path)
|
||||
{
|
||||
auto ret_val = m_edited_font->write_to_file(path);
|
||||
auto saved_font = m_edited_font->masked_character_set();
|
||||
auto ret_val = saved_font->write_to_file(path);
|
||||
if (!ret_val) {
|
||||
GUI::MessageBox::show(window(), "The font file could not be saved.", "Save failed", GUI::MessageBox::Type::Error);
|
||||
return false;
|
||||
|
|
|
@ -73,7 +73,6 @@ private:
|
|||
RefPtr<GUI::Widget> m_left_column_container;
|
||||
RefPtr<GUI::Widget> m_glyph_editor_container;
|
||||
RefPtr<GUI::ComboBox> m_weight_combobox;
|
||||
RefPtr<GUI::ComboBox> m_type_combobox;
|
||||
RefPtr<GUI::SpinBox> m_spacing_spinbox;
|
||||
RefPtr<GUI::SpinBox> m_baseline_spinbox;
|
||||
RefPtr<GUI::SpinBox> m_mean_line_spinbox;
|
||||
|
@ -87,6 +86,5 @@ private:
|
|||
|
||||
String m_path;
|
||||
Vector<String> m_font_weight_list;
|
||||
Vector<String> m_font_type_list;
|
||||
bool m_font_metadata { true };
|
||||
};
|
||||
|
|
|
@ -207,15 +207,6 @@
|
|||
text: "Fixed width"
|
||||
autosize: true
|
||||
}
|
||||
|
||||
@GUI::Widget {
|
||||
fixed_width: 12
|
||||
}
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "type_combobox"
|
||||
model_only: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ void GlyphEditorWidget::delete_glyph()
|
|||
{
|
||||
if (on_undo_event)
|
||||
on_undo_event();
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
for (int x = 0; x < bitmap.width(); x++)
|
||||
for (int y = 0; y < bitmap.height(); y++)
|
||||
bitmap.set_bit_at(x, y, false);
|
||||
|
@ -56,7 +56,7 @@ void GlyphEditorWidget::cut_glyph()
|
|||
|
||||
void GlyphEditorWidget::copy_glyph()
|
||||
{
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
u8 bits[bitmap.width()][bitmap.height()];
|
||||
for (int x = 0; x < bitmap.width(); x++) {
|
||||
for (int y = 0; y < bitmap.height(); y++) {
|
||||
|
@ -105,7 +105,7 @@ void GlyphEditorWidget::paste_glyph()
|
|||
}
|
||||
}
|
||||
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
for (int x = 0; x < min(bitmap.width(), buffer_width.value()); x++) {
|
||||
for (int y = 0; y < min(bitmap.height(), buffer_height.value()); y++) {
|
||||
if (bits[x][y])
|
||||
|
@ -138,7 +138,7 @@ void GlyphEditorWidget::paint_event(GUI::PaintEvent& event)
|
|||
for (int x = 1; x < font().max_glyph_width(); ++x)
|
||||
painter.draw_line({ x * m_scale, 0 }, { x * m_scale, font().glyph_height() * m_scale }, palette().threed_shadow2());
|
||||
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
|
||||
for (int y = 0; y < font().glyph_height(); ++y) {
|
||||
for (int x = 0; x < font().max_glyph_width(); ++x) {
|
||||
|
@ -155,7 +155,7 @@ void GlyphEditorWidget::paint_event(GUI::PaintEvent& event)
|
|||
|
||||
bool GlyphEditorWidget::is_glyph_empty()
|
||||
{
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
for (int x = 0; x < bitmap.width(); x++)
|
||||
for (int y = 0; y < bitmap.height(); y++)
|
||||
if (bitmap.bit_at(x, y))
|
||||
|
@ -176,7 +176,7 @@ void GlyphEditorWidget::mousedown_event(GUI::MouseEvent& event)
|
|||
draw_at_mouse(event);
|
||||
} else {
|
||||
memset(m_movable_bits, 0, sizeof(m_movable_bits));
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
for (int x = s_max_width; x < s_max_width + bitmap.width(); x++)
|
||||
for (int y = s_max_height; y < s_max_height + bitmap.height(); y++)
|
||||
m_movable_bits[x][y] = bitmap.bit_at(x - s_max_width, y - s_max_height);
|
||||
|
@ -221,7 +221,7 @@ void GlyphEditorWidget::draw_at_mouse(const GUI::MouseEvent& event)
|
|||
return;
|
||||
int x = (event.x() - 1) / m_scale;
|
||||
int y = (event.y() - 1) / m_scale;
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
if (x < 0 || x >= bitmap.width())
|
||||
return;
|
||||
if (y < 0 || y >= bitmap.height())
|
||||
|
@ -238,7 +238,7 @@ void GlyphEditorWidget::move_at_mouse(const GUI::MouseEvent& event)
|
|||
{
|
||||
int x_delta = ((event.x() - 1) / m_scale) - x_offset;
|
||||
int y_delta = ((event.y() - 1) / m_scale) - y_offset;
|
||||
auto bitmap = font().glyph(m_glyph).glyph_bitmap();
|
||||
auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
|
||||
if (abs(x_delta) > bitmap.width() || abs(y_delta) > bitmap.height())
|
||||
return;
|
||||
for (int x = 0; x < bitmap.width(); x++) {
|
||||
|
|
|
@ -25,7 +25,6 @@ void GlyphMapWidget::initialize(Gfx::BitmapFont& mutable_font)
|
|||
if (m_font == mutable_font)
|
||||
return;
|
||||
m_font = mutable_font;
|
||||
m_glyph_count = mutable_font.glyph_count();
|
||||
m_selected_glyph = 0;
|
||||
vertical_scrollbar().set_step(font().glyph_height() + m_vertical_spacing);
|
||||
}
|
||||
|
@ -33,6 +32,8 @@ void GlyphMapWidget::initialize(Gfx::BitmapFont& mutable_font)
|
|||
void GlyphMapWidget::resize_event(GUI::ResizeEvent& event)
|
||||
{
|
||||
int event_width = event.size().width() - this->vertical_scrollbar().width() - (frame_thickness() * 2) - m_horizontal_spacing;
|
||||
int event_height = event.size().height() - (frame_thickness() * 2);
|
||||
m_visible_glyphs = (event_width * event_height) / (font().max_glyph_width() * font().glyph_height());
|
||||
m_columns = max(event_width / (font().max_glyph_width() + m_horizontal_spacing), 1);
|
||||
m_rows = ceil_div(m_glyph_count, m_columns);
|
||||
|
||||
|
@ -90,7 +91,10 @@ void GlyphMapWidget::paint_event(GUI::PaintEvent& event)
|
|||
painter.set_font(font());
|
||||
painter.fill_rect(widget_inner_rect(), palette().inactive_window_title());
|
||||
|
||||
for (int glyph = 0; glyph < m_glyph_count; ++glyph) {
|
||||
int scroll_steps = this->vertical_scrollbar().value() / this->vertical_scrollbar().step();
|
||||
int first_visible_glyph = scroll_steps * columns();
|
||||
|
||||
for (int glyph = first_visible_glyph; glyph <= first_visible_glyph + m_visible_glyphs && glyph < m_glyph_count; ++glyph) {
|
||||
Gfx::IntRect outer_rect = get_outer_rect(glyph);
|
||||
Gfx::IntRect inner_rect(
|
||||
outer_rect.x() + m_horizontal_spacing / 2,
|
||||
|
@ -99,9 +103,9 @@ void GlyphMapWidget::paint_event(GUI::PaintEvent& event)
|
|||
font().glyph_height());
|
||||
if (glyph == m_selected_glyph) {
|
||||
painter.fill_rect(outer_rect, is_focused() ? palette().selection() : palette().inactive_selection());
|
||||
if (m_font->contains_glyph(glyph))
|
||||
if (m_font->raw_glyph_width(glyph))
|
||||
painter.draw_glyph(inner_rect.location(), glyph, is_focused() ? palette().selection_text() : palette().inactive_selection_text());
|
||||
} else if (m_font->contains_glyph(glyph)) {
|
||||
} else if (m_font->raw_glyph_width(glyph)) {
|
||||
painter.fill_rect(outer_rect, palette().base());
|
||||
painter.draw_glyph(inner_rect.location(), glyph, palette().base_text());
|
||||
}
|
||||
|
|
|
@ -41,10 +41,11 @@ private:
|
|||
Gfx::IntRect get_outer_rect(int glyph) const;
|
||||
|
||||
RefPtr<Gfx::BitmapFont> m_font;
|
||||
int m_glyph_count { 384 };
|
||||
int m_glyph_count { 0x110000 };
|
||||
int m_columns { 32 };
|
||||
int m_rows { 12 };
|
||||
int m_horizontal_spacing { 2 };
|
||||
int m_vertical_spacing { 2 };
|
||||
int m_selected_glyph { 0 };
|
||||
int m_visible_glyphs { 0 };
|
||||
};
|
||||
|
|
|
@ -134,30 +134,14 @@ NewFontDialog::NewFontDialog(GUI::Window* parent_window)
|
|||
|
||||
m_name_textbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::TextBox>("name_textbox");
|
||||
m_family_textbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::TextBox>("family_textbox");
|
||||
m_type_combobox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::ComboBox>("type_combobox");
|
||||
m_type_info_label = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::Label>("type_info_label");
|
||||
m_weight_combobox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::ComboBox>("weight_combobox");
|
||||
m_presentation_spinbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("presentation_spinbox");
|
||||
|
||||
m_type_info_label->set_text("\xE2\x84\xB9\t");
|
||||
m_type_info_label->set_tooltip("Font type governs maximum glyph count");
|
||||
|
||||
for (auto& it : GUI::font_weight_names)
|
||||
m_font_weight_list.append(it.name);
|
||||
m_weight_combobox->set_model(*GUI::ItemListModel<String>::create(m_font_weight_list));
|
||||
m_weight_combobox->set_selected_index(3);
|
||||
|
||||
StringBuilder type_count;
|
||||
for (int i = 0; i < Gfx::FontTypes::__Count; i++) {
|
||||
type_count.appendff("{} ({})",
|
||||
Gfx::BitmapFont::type_name_by_type(static_cast<Gfx::FontTypes>(i)),
|
||||
Gfx::BitmapFont::glyph_count_by_type(static_cast<Gfx::FontTypes>(i)));
|
||||
m_font_type_list.append(type_count.to_string());
|
||||
type_count.clear();
|
||||
}
|
||||
m_type_combobox->set_model(*GUI::ItemListModel<String>::create(m_font_type_list));
|
||||
m_type_combobox->set_selected_index(0);
|
||||
|
||||
m_presentation_spinbox->set_value(12);
|
||||
|
||||
m_font_properties_page->on_page_enter = [&]() {
|
||||
|
@ -225,7 +209,6 @@ void NewFontDialog::save_metadata()
|
|||
m_new_font_metadata.name = m_name_textbox->text();
|
||||
m_new_font_metadata.family = m_family_textbox->text();
|
||||
m_new_font_metadata.weight = GUI::name_to_weight(m_weight_combobox->text());
|
||||
m_new_font_metadata.type = static_cast<Gfx::FontTypes>(m_type_combobox->selected_index());
|
||||
m_new_font_metadata.presentation_size = m_presentation_spinbox->value();
|
||||
|
||||
m_new_font_metadata.baseline = m_baseline_spinbox->value();
|
||||
|
|
|
@ -36,7 +36,6 @@ private:
|
|||
u16 weight;
|
||||
String name;
|
||||
String family;
|
||||
Gfx::FontTypes type;
|
||||
bool is_fixed_width;
|
||||
} m_new_font_metadata;
|
||||
|
||||
|
@ -47,8 +46,6 @@ private:
|
|||
RefPtr<GUI::WizardPage> m_font_properties_page;
|
||||
RefPtr<GUI::TextBox> m_name_textbox;
|
||||
RefPtr<GUI::TextBox> m_family_textbox;
|
||||
RefPtr<GUI::ComboBox> m_type_combobox;
|
||||
RefPtr<GUI::Label> m_type_info_label;
|
||||
RefPtr<GUI::ComboBox> m_weight_combobox;
|
||||
RefPtr<GUI::SpinBox> m_presentation_spinbox;
|
||||
|
||||
|
@ -62,6 +59,5 @@ private:
|
|||
RefPtr<GUI::CheckBox> m_fixed_width_checkbox;
|
||||
|
||||
Vector<String> m_font_list;
|
||||
Vector<String> m_font_type_list;
|
||||
Vector<String> m_font_weight_list;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
}
|
||||
|
||||
@GUI::Widget {
|
||||
fixed_height: 160
|
||||
fixed_height: 138
|
||||
layout: @GUI::VerticalBoxLayout {
|
||||
}
|
||||
|
||||
|
@ -42,29 +42,6 @@
|
|||
fixed_height: 22
|
||||
}
|
||||
|
||||
@GUI::Widget {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
}
|
||||
|
||||
@GUI::Label {
|
||||
fixed_width: 100
|
||||
text_alignment: "CenterLeft"
|
||||
text: "Type:"
|
||||
}
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "type_combobox"
|
||||
fixed_width: 180
|
||||
model_only: true
|
||||
}
|
||||
|
||||
@GUI::Label {
|
||||
name: "type_info_label"
|
||||
text_alignment: "CenterLeft"
|
||||
autosize: true
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::Widget {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
}
|
||||
|
|
|
@ -52,7 +52,8 @@ int main(int argc, char** argv)
|
|||
|
||||
RefPtr<Gfx::BitmapFont> edited_font;
|
||||
if (path == nullptr) {
|
||||
edited_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone());
|
||||
auto bitmap_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone());
|
||||
edited_font = static_ptr_cast<Gfx::BitmapFont>(bitmap_font->unmasked_character_set());
|
||||
} else {
|
||||
auto bitmap_font = Gfx::BitmapFont::load_from_file(path);
|
||||
if (!bitmap_font) {
|
||||
|
@ -60,7 +61,7 @@ int main(int argc, char** argv)
|
|||
GUI::MessageBox::show(nullptr, message, "Font Editor", GUI::MessageBox::Type::Error);
|
||||
return 1;
|
||||
}
|
||||
edited_font = static_ptr_cast<Gfx::BitmapFont>(bitmap_font->clone());
|
||||
edited_font = static_ptr_cast<Gfx::BitmapFont>(bitmap_font->unmasked_character_set());
|
||||
if (!edited_font) {
|
||||
String message = String::formatted("Couldn't load font: {}\n", path);
|
||||
GUI::MessageBox::show(nullptr, message, "Font Editor", GUI::MessageBox::Type::Error);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue