7#include "draw_context_intf.hpp"
8#include "gfx_pipeline_box_vulkan_impl.hpp"
9#include "gfx_pipeline_image_vulkan_impl.hpp"
10#include "gfx_pipeline_SDF_vulkan_impl.hpp"
11#include "gfx_pipeline_override_vulkan_impl.hpp"
12#include "gfx_device_vulkan_intf.hpp"
13#include "../text/text.hpp"
14#include "../macros.hpp"
16hi_export_module(hikogui.GFX : draw_context_impl);
18hi_export
namespace hi {
inline namespace v1 {
20inline draw_context::draw_context(
22 vector_span<gfx_pipeline_box::vertex>& box_vertices,
23 vector_span<gfx_pipeline_image::vertex>& image_vertices,
24 vector_span<gfx_pipeline_SDF::vertex>& sdf_vertices,
25 vector_span<gfx_pipeline_override::vertex>& override_vertices) noexcept :
29 _box_vertices(&box_vertices),
30 _image_vertices(&image_vertices),
31 _sdf_vertices(&sdf_vertices),
32 _override_vertices(&override_vertices)
34 _box_vertices->clear();
35 _image_vertices->clear();
36 _sdf_vertices->clear();
37 _override_vertices->clear();
41draw_context::_draw_override(aarectangle
const& clipping_rectangle, quad box, draw_attributes
const& attributes)
const noexcept
43 if (_override_vertices->full()) {
45 ++global_counter<
"override::overflow">;
49 gfx_pipeline_override::device_shared::place_vertices(*_override_vertices, clipping_rectangle, box, attributes.fill_color, attributes.line_color);
53draw_context::_draw_box(aarectangle
const& clipping_rectangle, quad box, draw_attributes
const& attributes)
const noexcept
56 auto const border_radius = attributes.line_width * 0.5f;
58 attributes.border_side == hi::border_side::inside ? box - border_radius :
59 attributes.border_side == hi::border_side::outside ? box + border_radius :
62 auto const corner_radius =
63 attributes.border_side == hi::border_side::inside ? attributes.corner_radius - border_radius :
64 attributes.border_side == hi::border_side::outside ? attributes.corner_radius + border_radius :
65 attributes.corner_radius;
68 if (_box_vertices->full()) {
70 ++global_counter<
"draw_box::overflow">;
74 gfx_pipeline_box::device_shared::place_vertices(
78 attributes.fill_color,
79 attributes.line_color,
80 attributes.line_width,
84[[nodiscard]]
inline bool draw_context::_draw_image(
85 aarectangle
const& clipping_rectangle,
87 gfx_pipeline_image::paged_image
const& image)
const noexcept
89 hi_assert_not_null(_image_vertices);
91 if (image.state != gfx_pipeline_image::paged_image::state_type::uploaded) {
95 device->image_pipeline->place_vertices(*_image_vertices, clipping_rectangle, box, image);
99inline void draw_context::_draw_glyph(
100 aarectangle
const& clipping_rectangle,
104 draw_attributes
const& attributes)
const noexcept
106 hi_assert_not_null(_sdf_vertices);
108 if (_sdf_vertices->full()) {
109 auto box_attributes = attributes;
110 box_attributes.fill_color =
hi::color{1.0f, 0.0f, 1.0f};
111 _draw_box(clipping_rectangle, box, box_attributes);
112 ++global_counter<
"draw_glyph::overflow">;
116 auto const atlas_was_updated =
117 device->SDF_pipeline->place_vertices(*_sdf_vertices, clipping_rectangle, box, font, glyph, attributes.fill_color);
119 if (atlas_was_updated) {
120 device->SDF_pipeline->prepare_atlas_for_rendering();
124inline void draw_context::_draw_text(
125 aarectangle
const& clipping_rectangle,
126 matrix3
const& transform,
127 text_shaper
const& text,
128 draw_attributes
const& attributes)
const noexcept
130 hi_assert_not_null(_sdf_vertices);
132 auto atlas_was_updated =
false;
133 for (
auto const& c : text) {
134 auto const box = translate2{c.position} * c.metrics.bounding_rectangle;
135 auto const color = attributes.num_colors > 0 ? attributes.fill_color : quad_color{c.style->color};
137 if (not is_visible(c.general_category)) {
140 }
else if (_sdf_vertices->full()) {
141 auto box_attributes = attributes;
142 box_attributes.fill_color =
hi::color{1.0f, 0.0f, 1.0f};
143 _draw_box(clipping_rectangle, box, box_attributes);
144 ++global_counter<
"draw_glyph::overflow">;
148 atlas_was_updated |= device->SDF_pipeline->place_vertices(
149 *_sdf_vertices, clipping_rectangle, transform * box, *c.glyphs.font, c.glyphs.ids.front(), color);
152 if (atlas_was_updated) {
153 device->SDF_pipeline->prepare_atlas_for_rendering();
157inline void draw_context::_draw_text_selection(
158 aarectangle
const& clipping_rectangle,
159 matrix3
const& transform,
160 text_shaper
const& text,
161 text_selection
const& selection,
162 draw_attributes
const& attributes)
const noexcept
164 auto const[first, last] = selection.selection_indices();
165 auto const first_ = text.begin() + first;
166 auto const last_ = text.begin() + last;
167 hi_axiom(first_ <= text.end());
168 hi_axiom(last_ <= text.end());
169 hi_axiom(first_ <= last_);
171 for (
auto it = first_; it != last_; ++it) {
172 _draw_box(clipping_rectangle, transform * it->rectangle, attributes);
176inline void draw_context::_draw_text_insertion_cursor_empty(
177 aarectangle
const& clipping_rectangle,
178 matrix3
const& transform,
179 text_shaper
const& text,
180 draw_attributes
const& attributes)
const noexcept
182 auto const maximum_left =
std::round(text.rectangle().left() - 0.5f);
183 auto const maximum_right =
std::round(text.rectangle().right() - 0.5f);
184 auto const& only_line = text.lines()[0];
188 auto const left = only_line.paragraph_direction == unicode_bidi_class::L ? maximum_left : maximum_right;
190 auto const shape_I = aarectangle{point2{
left,
bottom}, point2{
left + 1.0f,
top}};
191 _draw_box(clipping_rectangle, transform * shape_I, attributes);
194inline void draw_context::_draw_text_insertion_cursor(
195 aarectangle
const& clipping_rectangle,
196 matrix3
const& transform,
197 text_shaper
const& text,
200 draw_attributes
const& attributes)
const noexcept
202 auto const maximum_left =
std::round(text.rectangle().left() - 0.5f);
203 auto const maximum_right =
std::round(text.rectangle().right() - 0.5f);
205 auto const it = text.get_it(cursor);
206 auto const& line = text.lines()[it->line_nr];
207 auto const ltr = it->direction == unicode_bidi_class::L;
208 auto const on_right = ltr == cursor.after();
215 auto const next_line_nr = it->line_nr + 1;
216 auto const line_ltr = line.paragraph_direction == unicode_bidi_class::L;
217 auto const end_of_line = line_ltr ? it->column_nr == line.columns.size() - 1 : it->column_nr == 0;
218 if (cursor.after() and end_of_line and next_line_nr < text.lines().size()) {
221 auto const& next_line = text.lines()[next_line_nr];
225 left = it->direction == unicode_bidi_class::L ? maximum_left : maximum_right;
229 left = std::clamp(
left, maximum_left - 1.0f, maximum_right + 1.0f);
232 auto const shape_I = aarectangle{point2{
left,
bottom}, point2{
left + 1.0f,
top}};
233 _draw_box(clipping_rectangle, transform * shape_I, attributes);
237 auto const shape_flag = ltr ? aarectangle{point2{
left + 1.0f,
top - 1.0f}, point2{
left + 3.0f,
top}} :
240 _draw_box(clipping_rectangle, transform * shape_flag, attributes);
244inline void draw_context::_draw_text_overwrite_cursor(
245 aarectangle
const& clipping_rectangle,
246 matrix3
const& transform,
247 text_shaper::char_const_iterator it,
248 draw_attributes
const& attributes)
const noexcept
250 auto const box =
ceil(it->rectangle) + 0.5f;
251 _draw_box(clipping_rectangle, transform * box, attributes);
254inline void draw_context::_draw_text_cursors(
255 aarectangle
const& clipping_rectangle,
256 matrix3
const& transform,
257 text_shaper
const& text,
258 text_cursor primary_cursor,
260 bool dead_character_mode,
261 draw_attributes
const& attributes)
const noexcept
263 hi_axiom(attributes.line_width == 0.0f);
267 return _draw_text_insertion_cursor_empty(clipping_rectangle, transform, text, attributes);
270 auto draw_flags =
false;
272 hi_assert_bounds(primary_cursor.index(), text);
274 if (dead_character_mode) {
275 hi_assert(primary_cursor.before());
276 auto cursor_attributes = attributes;
277 cursor_attributes.fill_color = attributes.line_color;
278 cursor_attributes.line_color = {};
279 return _draw_text_overwrite_cursor(
280 clipping_rectangle, transform, text.begin() + primary_cursor.index(), cursor_attributes);
283 if (overwrite_mode and primary_cursor.before()) {
284 auto cursor_attributes = attributes;
285 cursor_attributes.fill_color = {};
286 cursor_attributes.line_color = attributes.fill_color;
287 cursor_attributes.line_width = 1.0f;
288 return _draw_text_overwrite_cursor(
289 clipping_rectangle, transform, text.begin() + primary_cursor.index(), cursor_attributes);
293 auto const primary_it = text.begin() + primary_cursor.index();
294 auto const primary_ltr = primary_it->direction == unicode_bidi_class::L;
295 auto const primary_is_on_right = primary_ltr == primary_cursor.after();
296 auto const primary_is_on_left = not primary_is_on_right;
299 if (primary_cursor.start_of_text() or primary_cursor.end_of_text(text.size())) {
304 auto const secondary_cursor = primary_cursor.neighbor(text.size());
305 auto const secondary_it = text.begin() + secondary_cursor.index();
306 auto const secondary_ltr = secondary_it->direction == unicode_bidi_class::L;
307 auto const secondary_is_on_right = secondary_ltr == secondary_cursor.after();
308 auto const secondary_is_on_left = not secondary_is_on_right;
310 if (primary_is_on_right and secondary_is_on_left and text.move_right_char(primary_it) == secondary_it) {
313 }
else if (primary_is_on_left and secondary_is_on_right and text.move_left_char(primary_it) == secondary_it) {
319 auto cursor_attributes = attributes;
320 cursor_attributes.fill_color = attributes.line_color;
321 cursor_attributes.line_color = {};
322 _draw_text_insertion_cursor(clipping_rectangle, transform, text, secondary_cursor, draw_flags, cursor_attributes);
325 _draw_text_insertion_cursor(clipping_rectangle, transform, text, primary_cursor, draw_flags, attributes);
@ bottom
Align to the bottom.
@ left
Align the text to the left side.
@ rectangle
The gui_event has rectangle data.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
This is a RGBA floating point color.
Definition color_intf.hpp:49