lxgui
Loading...
Searching...
No Matches
gui_backdrop.cpp
1#include "lxgui/gui_backdrop.hpp"
2
3#include "lxgui/gui_frame.hpp"
4#include "lxgui/gui_manager.hpp"
5#include "lxgui/gui_material.hpp"
6#include "lxgui/gui_out.hpp"
7#include "lxgui/gui_quad.hpp"
8#include "lxgui/gui_renderer.hpp"
9#include "lxgui/utils_file_system.hpp"
10
11namespace lxgui::gui {
12
13backdrop::backdrop(frame& parent) : parent_(parent) {}
14
15void backdrop::copy_from(const backdrop& other) {
17 this->set_edge(other.get_edge_file());
18
20 this->set_tile_size(other.get_tile_size());
21
22 if (other.background_file_.empty())
24
26
27 if (other.edge_file_.empty())
28 this->set_edge_color(other.get_edge_color());
29
30 this->set_edge_size(other.get_edge_size());
31 this->set_edge_insets(other.get_edge_insets());
32}
33
34void backdrop::set_background(const std::string& background_file) {
35 if (background_file_ == background_file)
36 return;
37
38 is_cache_dirty_ = true;
39 background_color_ = color::empty;
40
41 if (background_file.empty()) {
42 background_texture_ = nullptr;
43 background_file_ = "";
44 return;
45 }
46
47 if (!utils::file_exists(background_file)) {
48 background_texture_ = nullptr;
49 background_file_ = "";
50
51 gui::out << gui::warning << "backdrop: "
52 << "Cannot find file: \"" << background_file << "\" for " << parent_.get_name()
53 << "'s backdrop background file. No background will be drawn." << std::endl;
54
55 return;
56 }
57
58 auto& renderer = parent_.get_manager().get_renderer();
59 background_texture_ = renderer.create_atlas_material("GUI", background_file);
60 if (!background_texture_) {
61 return;
62 }
63
64 tile_size_ = original_tile_size_ = static_cast<float>(background_texture_->get_rect().width());
65 background_file_ = background_file;
66}
67
68const std::string& backdrop::get_background_file() const {
69 return background_file_;
70}
71
73 if (background_color_ == c)
74 return;
75
76 is_cache_dirty_ = true;
77
78 background_texture_ = nullptr;
79 background_color_ = c;
80 background_file_ = "";
81
82 tile_size_ = original_tile_size_ = 256.0f;
83}
84
86 return background_color_;
87}
88
89void backdrop::set_background_tilling(bool is_tilling) {
90 if (is_background_tilling_ == is_tilling)
91 return;
92
93 is_background_tilling_ = is_tilling;
94 is_cache_dirty_ = true;
95}
96
98 return is_background_tilling_;
99}
100
101void backdrop::set_tile_size(float tile_size) {
102 if (tile_size_ == tile_size)
103 return;
104
105 tile_size_ = tile_size;
106 is_cache_dirty_ = true;
107}
108
110 return tile_size_;
111}
112
114 if (background_insets_ == insets)
115 return;
116
117 background_insets_ = insets;
118 is_cache_dirty_ = true;
119}
120
122 return background_insets_;
123}
124
126 if (edge_insets_ == insets)
127 return;
128
129 edge_insets_ = insets;
130 is_cache_dirty_ = true;
131}
132
134 return edge_insets_;
135}
136
137void backdrop::set_edge(const std::string& edge_file) {
138 if (edge_file == edge_file_)
139 return;
140
141 is_cache_dirty_ = true;
142 edge_color_ = color::empty;
143
144 if (edge_file.empty()) {
145 edge_texture_ = nullptr;
146 edge_file_ = "";
147 return;
148 }
149
150 if (!utils::file_exists(edge_file)) {
151 edge_texture_ = nullptr;
152 edge_file_ = "";
153
154 gui::out << gui::warning << "backdrop: "
155 << "Cannot find file: \"" << edge_file << "\" for " << parent_.get_name()
156 << "'s backdrop edge. No edge will be drawn." << std::endl;
157
158 return;
159 }
160
161 auto& renderer = parent_.get_manager().get_renderer();
162 edge_texture_ = renderer.create_atlas_material("GUI", edge_file);
163 if (!edge_texture_) {
164 return;
165 }
166
167 if (edge_texture_->get_rect().width() / edge_texture_->get_rect().height() != 8.0f) {
168 edge_texture_ = nullptr;
169 edge_file_ = "";
170
171 gui::out << gui::error << "backdrop: "
172 << "An edge texture width must be exactly 8 times greater than its height "
173 << "(in " << edge_file << "). No edge will be drawn for " << parent_.get_name()
174 << "'s backdrop." << std::endl;
175
176 return;
177 }
178
179 edge_size_ = original_edge_size_ = edge_texture_->get_rect().height();
180 edge_file_ = edge_file;
181}
182
183const std::string& backdrop::get_edge_file() const {
184 return edge_file_;
185}
186
188 if (edge_color_ == c)
189 return;
190
191 is_cache_dirty_ = true;
192 edge_texture_ = nullptr;
193 edge_color_ = c;
194 edge_file_ = "";
195
196 if (edge_size_ == 0.0f)
197 edge_size_ = 1.0f;
198
199 original_edge_size_ = 1.0f;
200}
201
203 return edge_color_;
204}
205
206void backdrop::set_edge_size(float edge_size) {
207 if (edge_size_ == edge_size)
208 return;
209
210 edge_size_ = edge_size;
211 is_cache_dirty_ = true;
212}
213
215 return edge_size_;
216}
217
219 if (vertex_color_ == c)
220 return;
221
222 vertex_color_ = c;
223 is_cache_dirty_ = true;
224}
225
226void backdrop::render() const {
227 float alpha = parent_.get_effective_alpha();
228 if (alpha != cache_alpha_)
229 is_cache_dirty_ = true;
230
231 auto& renderer = parent_.get_manager().get_renderer();
232 bool use_vertex_cache =
234
235 bool has_background = background_texture_ || background_color_ != color::empty;
236 bool has_edge = edge_texture_ || edge_color_ != color::empty;
237
238 if (has_background) {
239 if ((use_vertex_cache && !background_cache_) ||
240 (!use_vertex_cache && background_quads_.empty()))
241 is_cache_dirty_ = true;
242 }
243
244 if (has_edge) {
245 if ((use_vertex_cache && !edge_cache_) || (!use_vertex_cache && edge_quads_.empty()))
246 is_cache_dirty_ = true;
247 }
248
249 update_cache_();
250
251 if (has_background) {
252 if (use_vertex_cache && background_cache_)
253 renderer.render_cache(background_texture_.get(), *background_cache_);
254 else
255 renderer.render_quads(background_texture_.get(), background_quads_);
256 }
257
258 if (has_edge) {
259 if (use_vertex_cache && edge_cache_)
260 renderer.render_cache(edge_texture_.get(), *edge_cache_);
261 else
262 renderer.render_quads(edge_texture_.get(), edge_quads_);
263 }
264}
265
267 is_cache_dirty_ = true;
268}
269
270void backdrop::update_cache_() const {
271 if (!is_cache_dirty_)
272 return;
273
274 background_quads_.clear();
275 edge_quads_.clear();
276
277 color color = vertex_color_;
278
279 float alpha = parent_.get_effective_alpha();
280 color.a *= alpha;
281
282 update_background_(color);
283 update_edge_(color);
284
285 cache_alpha_ = alpha;
286 is_cache_dirty_ = false;
287}
288
290 const frame& parent,
291 std::vector<std::array<vertex, 4>>& output,
292 const bounds2f& source_uvs,
293 float tile_size,
294 bool is_rotated,
295 const color color,
296 const bounds2f& destination) {
297 const auto d_top_left = destination.top_left();
298 const auto s_top_left = source_uvs.top_left();
299 const float dest_width = destination.width();
300 const float dest_height = destination.height();
301
302 float sy = 0.0f;
303 while (sy < dest_height) {
304 float d_height = tile_size;
305 float s_height = source_uvs.height();
306 if (sy + tile_size > dest_height)
307 d_height = dest_height - sy;
308
309 float sx = 0.0f;
310 while (sx < dest_width) {
311 float d_width = tile_size;
312 float s_width = source_uvs.width();
313 if (sx + tile_size > dest_width)
314 d_width = dest_width - sx;
315
316 output.emplace_back();
317 auto& quad = output.back();
318
319 quad[0].pos = parent.round_to_pixel(d_top_left + vector2f(sx, sy));
320 quad[1].pos = parent.round_to_pixel(d_top_left + vector2f(sx + d_width, sy));
321 quad[2].pos = parent.round_to_pixel(d_top_left + vector2f(sx + d_width, sy + d_height));
322 quad[3].pos = parent.round_to_pixel(d_top_left + vector2f(sx, sy + d_height));
323
324 if (is_rotated) {
325 s_height *= d_width / tile_size;
326 s_width *= d_height / tile_size;
327 quad[0].uvs = s_top_left + vector2f(0.0f, s_height);
328 quad[1].uvs = s_top_left + vector2f(0.0f, 0.0f);
329 quad[2].uvs = s_top_left + vector2f(s_width, 0.0f);
330 quad[3].uvs = s_top_left + vector2f(s_width, s_height);
331 } else {
332 s_width *= d_width / tile_size;
333 s_height *= d_height / tile_size;
334 quad[0].uvs = s_top_left + vector2f(0.0f, 0.0f);
335 quad[1].uvs = s_top_left + vector2f(s_width, 0.0f);
336 quad[2].uvs = s_top_left + vector2f(s_width, s_height);
337 quad[3].uvs = s_top_left + vector2f(0.0f, s_height);
338 }
339
340 quad[0].col = quad[1].col = quad[2].col = quad[3].col = color;
341
342 sx += d_width;
343 }
344
345 sy += d_height;
346 }
347}
348
349void backdrop::update_background_(color c) const {
350 if (!background_texture_ && background_color_ == color::empty)
351 return;
352
353 if (!background_texture_)
354 c *= background_color_;
355
356 auto borders = parent_.get_borders();
357 borders.left += background_insets_.left;
358 borders.right -= background_insets_.right;
359 borders.top += background_insets_.top;
360 borders.bottom -= background_insets_.bottom;
361
362 auto& renderer = parent_.get_manager().get_renderer();
363
364 if (background_texture_) {
365 const vector2f canvas_tl = background_texture_->get_canvas_uv(vector2f(0.0f, 0.0f), true);
366 const vector2f canvas_br = background_texture_->get_canvas_uv(vector2f(1.0f, 1.0f), true);
367 const bounds2f canvas_uvs = bounds2f(canvas_tl.x, canvas_br.x, canvas_tl.y, canvas_br.y);
368
369 float rounded_tile_size =
371
372 if (background_texture_->is_in_atlas() && is_background_tilling_ &&
373 rounded_tile_size > 1.0f) {
375 parent_, background_quads_, canvas_uvs, rounded_tile_size, false, c, borders);
376 } else {
377 background_quads_.emplace_back();
378 auto& quad = background_quads_.back();
379
380 quad[0].pos = parent_.round_to_pixel(borders.top_left());
381 quad[1].pos = parent_.round_to_pixel(borders.top_right());
382 quad[2].pos = parent_.round_to_pixel(borders.bottom_right());
383 quad[3].pos = parent_.round_to_pixel(borders.bottom_left());
384 quad[0].uvs = canvas_uvs.top_left();
385 quad[1].uvs = canvas_uvs.top_right();
386 quad[2].uvs = canvas_uvs.bottom_right();
387 quad[3].uvs = canvas_uvs.bottom_left();
388 quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
389 }
390 } else {
391 background_quads_.emplace_back();
392 auto& quad = background_quads_.back();
393
394 quad[0].pos = parent_.round_to_pixel(borders.top_left());
395 quad[1].pos = parent_.round_to_pixel(borders.top_right());
396 quad[2].pos = parent_.round_to_pixel(borders.bottom_right());
397 quad[3].pos = parent_.round_to_pixel(borders.bottom_left());
398 quad[0].uvs = vector2f(0.0f, 0.0f);
399 quad[1].uvs = vector2f(0.0f, 0.0f);
400 quad[2].uvs = vector2f(0.0f, 0.0f);
401 quad[3].uvs = vector2f(0.0f, 0.0f);
402
403 quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
404 }
405
406 if (renderer.is_vertex_cache_enabled() && !renderer.is_quad_batching_enabled()) {
407 if (!background_cache_)
408 background_cache_ = renderer.create_vertex_cache(vertex_cache::type::quads);
409
410 background_cache_->update(background_quads_[0].data(), background_quads_.size() * 4);
411 background_quads_.clear();
412 }
413}
414
415void backdrop::update_edge_(color c) const {
416 if (!edge_texture_ && edge_color_ == color::empty)
417 return;
418
419 if (!edge_texture_)
420 c *= edge_color_;
421
422 constexpr float uv_step = 1.0f / 8.0f;
423 auto borders = parent_.get_borders();
424 borders.left += edge_insets_.left;
425 borders.right -= edge_insets_.right;
426 borders.top += edge_insets_.top;
427 borders.bottom -= edge_insets_.bottom;
428
429 auto& renderer = parent_.get_manager().get_renderer();
430 const float rounded_edge_size =
432
433 auto repeat_wrap_edge = [&](const bounds2f& source_uvs, bool is_rotated,
434 const bounds2f& destination) {
435 if (edge_texture_) {
436 const vector2f canvas_tl = edge_texture_->get_canvas_uv(source_uvs.top_left(), true);
437 const vector2f canvas_br =
438 edge_texture_->get_canvas_uv(source_uvs.bottom_right(), true);
439 const bounds2f canvas_uvs =
440 bounds2f(canvas_tl.x, canvas_br.x, canvas_tl.y, canvas_br.y);
441
442 if (edge_texture_->is_in_atlas() && rounded_edge_size > 1.0f) {
444 parent_, edge_quads_, canvas_uvs, rounded_edge_size, is_rotated, c,
445 destination);
446 } else {
447 edge_quads_.emplace_back();
448 auto& quad = edge_quads_.back();
449
450 quad[0].pos = parent_.round_to_pixel(destination.top_left());
451 quad[1].pos = parent_.round_to_pixel(destination.top_right());
452 quad[2].pos = parent_.round_to_pixel(destination.bottom_right());
453 quad[3].pos = parent_.round_to_pixel(destination.bottom_left());
454
455 if (rounded_edge_size <= 1.0f) {
456 quad[0].uvs = canvas_uvs.top_left();
457 quad[1].uvs = canvas_uvs.top_right();
458 quad[2].uvs = canvas_uvs.bottom_right();
459 quad[3].uvs = canvas_uvs.bottom_left();
460 } else {
461 if (is_rotated) {
462 float factor = destination.width() / rounded_edge_size;
463 quad[0].uvs =
464 canvas_uvs.top_left() + vector2f(0.0, factor * canvas_uvs.height());
465 quad[1].uvs = canvas_uvs.top_left();
466 quad[2].uvs = canvas_uvs.top_right();
467 quad[3].uvs =
468 canvas_uvs.top_right() + vector2f(0.0, factor * canvas_uvs.height());
469 } else {
470 float factor = destination.height() / rounded_edge_size;
471 quad[0].uvs = canvas_uvs.top_left();
472 quad[1].uvs = canvas_uvs.top_right();
473 quad[2].uvs =
474 canvas_uvs.top_right() + vector2f(0.0, factor * canvas_uvs.height());
475 quad[3].uvs =
476 canvas_uvs.top_left() + vector2f(0.0, factor * canvas_uvs.height());
477 }
478 }
479
480 quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
481 }
482 } else {
483 edge_quads_.emplace_back();
484 auto& quad = edge_quads_.back();
485
486 quad[0].pos = parent_.round_to_pixel(destination.top_left());
487 quad[1].pos = parent_.round_to_pixel(destination.top_right());
488 quad[2].pos = parent_.round_to_pixel(destination.bottom_right());
489 quad[3].pos = parent_.round_to_pixel(destination.bottom_left());
490 quad[0].uvs = vector2f(0.0f, 0.0f);
491 quad[1].uvs = vector2f(0.0f, 0.0f);
492 quad[2].uvs = vector2f(0.0f, 0.0f);
493 quad[3].uvs = vector2f(0.0f, 0.0f);
494 quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
495 }
496 };
497
498 // Left edge
499 repeat_wrap_edge(
500 bounds2f(0.0f, uv_step, 0.0f, 1.0f), false,
501 bounds2f(
502 borders.left, borders.left + rounded_edge_size, borders.top + rounded_edge_size,
503 borders.bottom - rounded_edge_size));
504
505 // Right edge
506 repeat_wrap_edge(
507 bounds2f(uv_step, 2.0f * uv_step, 0.0f, 1.0f), false,
508 bounds2f(
509 borders.right - rounded_edge_size, borders.right, borders.top + rounded_edge_size,
510 borders.bottom - rounded_edge_size));
511
512 // Top edge
513 repeat_wrap_edge(
514 bounds2f(2.0f * uv_step, 3.0f * uv_step, 0.0f, 1.0f), true,
515 bounds2f(
516 borders.left + rounded_edge_size, borders.right - rounded_edge_size, borders.top,
517 borders.top + rounded_edge_size));
518
519 // Bottom edge
520 repeat_wrap_edge(
521 bounds2f(3.0f * uv_step, 4.0f * uv_step, 0.0f, 1.0f), true,
522 bounds2f(
523 borders.left + rounded_edge_size, borders.right - rounded_edge_size,
524 borders.bottom - rounded_edge_size, borders.bottom));
525
526 // Top-left corner
527 repeat_wrap_edge(
528 bounds2f(4.0f * uv_step, 5.0f * uv_step, 0.0f, 1.0f), false,
529 bounds2f(
530 borders.left, borders.left + rounded_edge_size, borders.top,
531 borders.top + rounded_edge_size));
532
533 // Top-right corner
534 repeat_wrap_edge(
535 bounds2f(5.0f * uv_step, 6.0f * uv_step, 0.0f, 1.0f), false,
536 bounds2f(
537 borders.right - rounded_edge_size, borders.right, borders.top,
538 borders.top + rounded_edge_size));
539
540 // Bottom-left corner
541 repeat_wrap_edge(
542 bounds2f(6.0f * uv_step, 7.0f * uv_step, 0.0f, 1.0f), false,
543 bounds2f(
544 borders.left, borders.left + rounded_edge_size, borders.bottom - rounded_edge_size,
545 borders.bottom));
546
547 // Bottom-right corner
548 repeat_wrap_edge(
549 bounds2f(7.0f * uv_step, 8.0f * uv_step, 0.0f, 1.0f), false,
550 bounds2f(
551 borders.right - rounded_edge_size, borders.right, borders.bottom - rounded_edge_size,
552 borders.bottom));
553
554 if (renderer.is_vertex_cache_enabled() && !renderer.is_quad_batching_enabled()) {
555 if (!edge_cache_)
556 edge_cache_ = renderer.create_vertex_cache(vertex_cache::type::quads);
557
558 edge_cache_->update(edge_quads_[0].data(), edge_quads_.size() * 4);
559 edge_quads_.clear();
560 }
561}
562
563} // namespace lxgui::gui
Draws borders and background of a frame.
bool is_background_tilling() const
Checks if tilling is enabled for the background texture.
float get_tile_size() const
Returns this backdrop's tile size.
color get_edge_color() const
Returns the edge color.
void set_tile_size(float tile_size)
Sets the apparent tile size.
void set_edge_color(const color &c)
Sets the edge color.
const bounds2f & get_edge_insets() const
Returns this backdrop's edge insets.
float get_edge_size() const
Returns this backdrop's edge size.
void set_edge_size(float edge_size)
Sets the apparent edge size.
void set_vertex_color(const color &c)
Sets the color to be multiplied to all drawn vertices.
const bounds2f & get_background_insets() const
Returns this backdrop's background insets.
const std::string & get_edge_file() const
Returns this backdrop's edge file.
void set_edge_insets(const bounds2f &insets)
Sets insets for the edge texture.
backdrop(frame &parent)
Constructor.
color get_background_color() const
Returns the background color.
void set_background_insets(const bounds2f &insets)
Sets insets for the background texture.
void copy_from(const backdrop &other)
Copies a backdrop's parameters into this one (inheritance).
void set_edge(const std::string &edge_file)
Sets the edge/corner texture.
const std::string & get_background_file() const
Returns this backdrop's background file.
void set_background(const std::string &background_file)
Sets the background texture.
void set_background_tilling(bool is_tilling)
Enables tilling for the background texture.
void set_background_color(const color &c)
Sets the background color.
void notify_borders_updated() const
Tells this backdrop that its parent frame has changed dimensions.
void render() const
Renders this backdrop on the current render target.
Holds a single color (float RGBA, 128 bits)
Definition gui_color.hpp:13
static const color empty
Definition gui_color.hpp:43
A region that can contain other regions and react to events.
const renderer & get_renderer() const
Returns the renderer implementation.
manager & get_manager()
Returns this region's manager.
const bounds2f & get_borders() const
Returns this region's borders.
float get_effective_alpha() const
Returns this region's effective alpha (opacity).
float round_to_pixel(float value, utils::rounding_method method=utils::rounding_method::nearest) const
Round an absolute position on screen to the nearest physical pixel.
const std::string & get_name() const
Returns this region's name.
Abstract type for implementation specific management.
bool is_quad_batching_enabled() const
Checks if the renderer has quad render batching enabled.
std::shared_ptr< material > create_atlas_material(const std::string &atlas_category, const std::string &file_name, material::filter filt=material::filter::none)
Creates a new material from a texture file.
void render_cache(const material *mat, const vertex_cache &cache, const matrix4f &model_transform=matrix4f::identity)
Renders a vertex cache.
void render_quads(const material *mat, const std::vector< std::array< vertex, 4 > > &quad_list)
Renders a set of quads.
bool is_vertex_cache_enabled() const
Checks if the renderer has enabled support for vertex caches.
@ quads
3 vertices per element
vector2< float > vector2f
Holds 2D coordinates (as floats)
bounds2< float > bounds2f
Holds 2D bounds of a region (as floats).
std::ostream out
void repeat_wrap(const frame &parent, std::vector< std::array< vertex, 4 > > &output, const bounds2f &source_uvs, float tile_size, bool is_rotated, const color color, const bounds2f &destination)
const std::string warning
Definition gui_out.cpp:6
const std::string error
Definition gui_out.cpp:7
@ nearest_not_zero
Equivalent to round() but only returns 0 if input is exactly 0.
bool file_exists(const std::string &file)
T height() const noexcept
T width() const noexcept
vector2< T > top_left() const noexcept
Simple structure holding four vertices and a material.
Definition gui_quad.hpp:18