Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "GlyphEditorWidget.h"
9#include <LibGUI/Painter.h>
10#include <LibGfx/Font/BitmapFont.h>
11#include <LibGfx/Palette.h>
12
13REGISTER_WIDGET(FontEditor, GlyphEditorWidget);
14
15namespace FontEditor {
16
17void GlyphEditorWidget::set_font(Gfx::BitmapFont& mutable_font)
18{
19 if (m_font == mutable_font)
20 return;
21 m_font = mutable_font;
22}
23
24void GlyphEditorWidget::set_glyph(int glyph)
25{
26 if (m_glyph == glyph)
27 return;
28 m_glyph = glyph;
29 update();
30}
31
32void GlyphEditorWidget::paint_event(GUI::PaintEvent& event)
33{
34 GUI::Frame::paint_event(event);
35
36 GUI::Painter painter(*this);
37 painter.add_clip_rect(frame_inner_rect());
38 painter.add_clip_rect(event.rect());
39 painter.fill_rect(frame_inner_rect(), palette().base());
40 painter.translate(frame_thickness(), frame_thickness());
41
42 painter.translate(-1, -1);
43 for (int y = 1; y < font().glyph_height(); ++y) {
44 int y_below = y - 1;
45 bool bold_line = y_below == font().baseline() || y_below == font().mean_line();
46 painter.draw_line({ 0, y * m_scale }, { font().max_glyph_width() * m_scale, y * m_scale }, palette().threed_shadow2(), bold_line ? 2 : 1);
47 }
48
49 for (int x = 1; x < font().max_glyph_width(); ++x)
50 painter.draw_line({ x * m_scale, 0 }, { x * m_scale, font().glyph_height() * m_scale }, palette().threed_shadow2());
51
52 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
53
54 for (int y = 0; y < font().glyph_height(); ++y) {
55 for (int x = 0; x < font().max_glyph_width(); ++x) {
56 Gfx::IntRect rect { x * m_scale, y * m_scale, m_scale, m_scale };
57 if (x >= font().raw_glyph_width(m_glyph)) {
58 painter.fill_rect(rect, palette().threed_shadow1());
59 } else {
60 if (bitmap.bit_at(x, y))
61 painter.fill_rect(rect, palette().base_text());
62 }
63 }
64 }
65}
66
67bool GlyphEditorWidget::is_glyph_empty()
68{
69 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
70 for (int x = 0; x < font().max_glyph_width(); x++)
71 for (int y = 0; y < font().glyph_height(); y++)
72 if (bitmap.bit_at(x, y))
73 return false;
74 return true;
75}
76
77void GlyphEditorWidget::mousedown_event(GUI::MouseEvent& event)
78{
79 if ((event.x() - 1) / m_scale + 1 > font().raw_glyph_width(m_glyph))
80 return;
81 if (mode() == Move && is_glyph_empty())
82 return;
83 m_is_clicking_valid_cell = true;
84 if (on_undo_event)
85 on_undo_event();
86 if (mode() == Paint) {
87 draw_at_mouse(event);
88 } else {
89 memset(m_movable_bits, 0, sizeof(m_movable_bits));
90 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
91 for (int x = 0; x < bitmap.width(); x++) {
92 for (int y = 0; y < bitmap.height(); y++) {
93 int movable_x = Gfx::GlyphBitmap::max_width() + x;
94 int movable_y = Gfx::GlyphBitmap::max_height() + y;
95 m_movable_bits[movable_x][movable_y] = bitmap.bit_at(x, y);
96 }
97 }
98 m_scaled_offset_x = (event.x() - 1) / m_scale;
99 m_scaled_offset_y = (event.y() - 1) / m_scale;
100 move_at_mouse(event);
101 }
102}
103
104void GlyphEditorWidget::mouseup_event(GUI::MouseEvent&)
105{
106 if (!m_is_clicking_valid_cell)
107 return;
108 m_is_clicking_valid_cell = false;
109}
110
111void GlyphEditorWidget::mousemove_event(GUI::MouseEvent& event)
112{
113 if (!m_is_clicking_valid_cell)
114 return;
115 if (!(event.buttons() & (GUI::MouseButton::Primary | GUI::MouseButton::Secondary)))
116 return;
117 if (mode() == Paint)
118 draw_at_mouse(event);
119 else
120 move_at_mouse(event);
121}
122
123void GlyphEditorWidget::enter_event(Core::Event&)
124{
125 if (mode() == Move)
126 set_override_cursor(Gfx::StandardCursor::Move);
127 else
128 set_override_cursor(Gfx::StandardCursor::None);
129}
130
131void GlyphEditorWidget::draw_at_mouse(GUI::MouseEvent const& event)
132{
133 bool set = event.buttons() & GUI::MouseButton::Primary;
134 bool unset = event.buttons() & GUI::MouseButton::Secondary;
135 if (!(set ^ unset))
136 return;
137 int x = (event.x() - 1) / m_scale;
138 int y = (event.y() - 1) / m_scale;
139 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
140 if (x < 0 || x >= bitmap.width())
141 return;
142 if (y < 0 || y >= bitmap.height())
143 return;
144 if (bitmap.bit_at(x, y) == set)
145 return;
146 bitmap.set_bit_at(x, y, set);
147 if (on_glyph_altered)
148 on_glyph_altered(m_glyph);
149 update();
150}
151
152void GlyphEditorWidget::move_at_mouse(GUI::MouseEvent const& event)
153{
154 int x_delta = ((event.x() - 1) / m_scale) - m_scaled_offset_x;
155 int y_delta = ((event.y() - 1) / m_scale) - m_scaled_offset_y;
156 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
157 if (abs(x_delta) > bitmap.width() || abs(y_delta) > bitmap.height())
158 return;
159 for (int x = 0; x < bitmap.width(); x++) {
160 for (int y = 0; y < bitmap.height(); y++) {
161 int movable_x = Gfx::GlyphBitmap::max_width() + x - x_delta;
162 int movable_y = Gfx::GlyphBitmap::max_height() + y - y_delta;
163 bitmap.set_bit_at(x, y, m_movable_bits[movable_x][movable_y]);
164 }
165 }
166 if (on_glyph_altered)
167 on_glyph_altered(m_glyph);
168 update();
169}
170
171static Vector<Vector<u8>> glyph_as_matrix(Gfx::GlyphBitmap const& bitmap)
172{
173 Vector<Vector<u8>> result;
174 result.ensure_capacity(bitmap.height());
175
176 for (int y = 0; y < bitmap.height(); y++) {
177 result.empend();
178 auto& row = result.last();
179 row.ensure_capacity(bitmap.width());
180 for (int x = 0; x < bitmap.width(); x++) {
181 row.append(bitmap.bit_at(x, y));
182 }
183 }
184
185 return result;
186}
187
188void GlyphEditorWidget::rotate_90(Gfx::RotationDirection direction)
189{
190 if (on_undo_event)
191 on_undo_event();
192
193 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
194 auto matrix = glyph_as_matrix(bitmap);
195 auto clockwise = direction == Gfx::RotationDirection::Clockwise;
196
197 for (int y = 0; y < bitmap.height(); y++) {
198 for (int x = 0; x < bitmap.width(); x++) {
199 int source_x = clockwise ? y : max(bitmap.width() - 1 - y, 0);
200 int source_y = clockwise ? bitmap.width() - 1 - x : x;
201 bool value = false;
202 if (source_x < bitmap.width() && source_y < bitmap.height()) {
203 value = (!clockwise && y >= bitmap.width()) ? false : matrix[source_y][source_x];
204 }
205 bitmap.set_bit_at(x, y, value);
206 }
207 }
208
209 if (on_glyph_altered)
210 on_glyph_altered(m_glyph);
211 update();
212}
213
214void GlyphEditorWidget::flip(Gfx::Orientation orientation)
215{
216 if (on_undo_event)
217 on_undo_event();
218
219 auto bitmap = font().raw_glyph(m_glyph).glyph_bitmap();
220 auto matrix = glyph_as_matrix(bitmap);
221 auto vertical = orientation == Gfx::Orientation::Vertical;
222
223 for (int y = 0; y < bitmap.height(); y++) {
224 for (int x = 0; x < bitmap.width(); x++) {
225 int source_x = vertical ? x : bitmap.width() - 1 - x;
226 int source_y = vertical ? bitmap.height() - 1 - y : y;
227 bool value = matrix[source_y][source_x];
228 bitmap.set_bit_at(x, y, value);
229 }
230 }
231
232 if (on_glyph_altered)
233 on_glyph_altered(m_glyph);
234 update();
235}
236
237int GlyphEditorWidget::preferred_width() const
238{
239 return frame_thickness() * 2 + font().max_glyph_width() * m_scale - 1;
240}
241
242int GlyphEditorWidget::preferred_height() const
243{
244 return frame_thickness() * 2 + font().glyph_height() * m_scale - 1;
245}
246
247void GlyphEditorWidget::set_scale(int scale)
248{
249 if (m_scale == scale)
250 return;
251 m_scale = clamp(scale, 1, 15);
252 update();
253}
254
255}