lxgui
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 
11 namespace lxgui::gui {
12 
13 backdrop::backdrop(frame& parent) : parent_(parent) {}
14 
15 void backdrop::copy_from(const backdrop& other) {
16  this->set_background(other.get_background_file());
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 
34 void 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 
61  tile_size_ = original_tile_size_ = static_cast<float>(background_texture_->get_rect().width());
62  background_file_ = background_file;
63 }
64 
65 const std::string& backdrop::get_background_file() const {
66  return background_file_;
67 }
68 
70  if (background_color_ == c)
71  return;
72 
73  is_cache_dirty_ = true;
74 
75  background_texture_ = nullptr;
76  background_color_ = c;
77  background_file_ = "";
78 
79  tile_size_ = original_tile_size_ = 256.0f;
80 }
81 
83  return background_color_;
84 }
85 
86 void backdrop::set_background_tilling(bool is_tilling) {
87  if (is_background_tilling_ == is_tilling)
88  return;
89 
90  is_background_tilling_ = is_tilling;
91  is_cache_dirty_ = true;
92 }
93 
95  return is_background_tilling_;
96 }
97 
98 void backdrop::set_tile_size(float tile_size) {
99  if (tile_size_ == tile_size)
100  return;
101 
102  tile_size_ = tile_size;
103  is_cache_dirty_ = true;
104 }
105 
106 float backdrop::get_tile_size() const {
107  return tile_size_;
108 }
109 
111  if (background_insets_ == insets)
112  return;
113 
114  background_insets_ = insets;
115  is_cache_dirty_ = true;
116 }
117 
119  return background_insets_;
120 }
121 
122 void backdrop::set_edge_insets(const bounds2f& insets) {
123  if (edge_insets_ == insets)
124  return;
125 
126  edge_insets_ = insets;
127  is_cache_dirty_ = true;
128 }
129 
131  return edge_insets_;
132 }
133 
134 void backdrop::set_edge(const std::string& edge_file) {
135  if (edge_file == edge_file_)
136  return;
137 
138  is_cache_dirty_ = true;
139  edge_color_ = color::empty;
140 
141  if (edge_file.empty()) {
142  edge_texture_ = nullptr;
143  edge_file_ = "";
144  return;
145  }
146 
147  if (!utils::file_exists(edge_file)) {
148  edge_texture_ = nullptr;
149  edge_file_ = "";
150 
151  gui::out << gui::warning << "backdrop: "
152  << "Cannot find file: \"" << edge_file << "\" for " << parent_.get_name()
153  << "'s backdrop edge. No edge will be drawn." << std::endl;
154 
155  return;
156  }
157 
158  auto& renderer = parent_.get_manager().get_renderer();
159  edge_texture_ = renderer.create_atlas_material("GUI", edge_file);
160 
161  if (edge_texture_->get_rect().width() / edge_texture_->get_rect().height() != 8.0f) {
162  edge_texture_ = nullptr;
163  edge_file_ = "";
164 
165  gui::out << gui::error << "backdrop: "
166  << "An edge texture width must be exactly 8 times greater than its height "
167  << "(in " << edge_file << "). No edge will be drawn for " << parent_.get_name()
168  << "'s backdrop." << std::endl;
169 
170  return;
171  }
172 
173  edge_size_ = original_edge_size_ = edge_texture_->get_rect().height();
174  edge_file_ = edge_file;
175 }
176 
177 const std::string& backdrop::get_edge_file() const {
178  return edge_file_;
179 }
180 
182  if (edge_color_ == c)
183  return;
184 
185  is_cache_dirty_ = true;
186  edge_texture_ = nullptr;
187  edge_color_ = c;
188  edge_file_ = "";
189 
190  if (edge_size_ == 0.0f)
191  edge_size_ = 1.0f;
192 
193  original_edge_size_ = 1.0f;
194 }
195 
197  return edge_color_;
198 }
199 
200 void backdrop::set_edge_size(float edge_size) {
201  if (edge_size_ == edge_size)
202  return;
203 
204  edge_size_ = edge_size;
205  is_cache_dirty_ = true;
206 }
207 
208 float backdrop::get_edge_size() const {
209  return edge_size_;
210 }
211 
213  if (vertex_color_ == c)
214  return;
215 
216  vertex_color_ = c;
217  is_cache_dirty_ = true;
218 }
219 
220 void backdrop::render() const {
221  float alpha = parent_.get_effective_alpha();
222  if (alpha != cache_alpha_)
223  is_cache_dirty_ = true;
224 
225  auto& renderer = parent_.get_manager().get_renderer();
226  bool use_vertex_cache =
228 
229  bool has_background = background_texture_ || background_color_ != color::empty;
230  bool has_edge = edge_texture_ || edge_color_ != color::empty;
231 
232  if (has_background) {
233  if ((use_vertex_cache && !background_cache_) ||
234  (!use_vertex_cache && background_quads_.empty()))
235  is_cache_dirty_ = true;
236  }
237 
238  if (has_edge) {
239  if ((use_vertex_cache && !edge_cache_) || (!use_vertex_cache && edge_quads_.empty()))
240  is_cache_dirty_ = true;
241  }
242 
243  update_cache_();
244 
245  if (has_background) {
246  if (use_vertex_cache && background_cache_)
247  renderer.render_cache(background_texture_.get(), *background_cache_);
248  else
249  renderer.render_quads(background_texture_.get(), background_quads_);
250  }
251 
252  if (has_edge) {
253  if (use_vertex_cache && edge_cache_)
254  renderer.render_cache(edge_texture_.get(), *edge_cache_);
255  else
256  renderer.render_quads(edge_texture_.get(), edge_quads_);
257  }
258 }
259 
261  is_cache_dirty_ = true;
262 }
263 
264 void backdrop::update_cache_() const {
265  if (!is_cache_dirty_)
266  return;
267 
268  background_quads_.clear();
269  edge_quads_.clear();
270 
271  color color = vertex_color_;
272 
273  float alpha = parent_.get_effective_alpha();
274  color.a *= alpha;
275 
276  update_background_(color);
277  update_edge_(color);
278 
279  cache_alpha_ = alpha;
280  is_cache_dirty_ = false;
281 }
282 
284  const frame& parent,
285  std::vector<std::array<vertex, 4>>& output,
286  const bounds2f& source_uvs,
287  float tile_size,
288  bool is_rotated,
289  const color color,
290  const bounds2f& destination) {
291  const auto d_top_left = destination.top_left();
292  const auto s_top_left = source_uvs.top_left();
293  const float dest_width = destination.width();
294  const float dest_height = destination.height();
295 
296  float sy = 0.0f;
297  while (sy < dest_height) {
298  float d_height = tile_size;
299  float s_height = source_uvs.height();
300  if (sy + tile_size > dest_height)
301  d_height = dest_height - sy;
302 
303  float sx = 0.0f;
304  while (sx < dest_width) {
305  float d_width = tile_size;
306  float s_width = source_uvs.width();
307  if (sx + tile_size > dest_width)
308  d_width = dest_width - sx;
309 
310  output.emplace_back();
311  auto& quad = output.back();
312 
313  quad[0].pos = parent.round_to_pixel(d_top_left + vector2f(sx, sy));
314  quad[1].pos = parent.round_to_pixel(d_top_left + vector2f(sx + d_width, sy));
315  quad[2].pos = parent.round_to_pixel(d_top_left + vector2f(sx + d_width, sy + d_height));
316  quad[3].pos = parent.round_to_pixel(d_top_left + vector2f(sx, sy + d_height));
317 
318  if (is_rotated) {
319  s_height *= d_width / tile_size;
320  s_width *= d_height / tile_size;
321  quad[0].uvs = s_top_left + vector2f(0.0f, s_height);
322  quad[1].uvs = s_top_left + vector2f(0.0f, 0.0f);
323  quad[2].uvs = s_top_left + vector2f(s_width, 0.0f);
324  quad[3].uvs = s_top_left + vector2f(s_width, s_height);
325  } else {
326  s_width *= d_width / tile_size;
327  s_height *= d_height / tile_size;
328  quad[0].uvs = s_top_left + vector2f(0.0f, 0.0f);
329  quad[1].uvs = s_top_left + vector2f(s_width, 0.0f);
330  quad[2].uvs = s_top_left + vector2f(s_width, s_height);
331  quad[3].uvs = s_top_left + vector2f(0.0f, s_height);
332  }
333 
334  quad[0].col = quad[1].col = quad[2].col = quad[3].col = color;
335 
336  sx += d_width;
337  }
338 
339  sy += d_height;
340  }
341 }
342 
343 void backdrop::update_background_(color c) const {
344  if (!background_texture_ && background_color_ == color::empty)
345  return;
346 
347  if (!background_texture_)
348  c *= background_color_;
349 
350  auto borders = parent_.get_borders();
351  borders.left += background_insets_.left;
352  borders.right -= background_insets_.right;
353  borders.top += background_insets_.top;
354  borders.bottom -= background_insets_.bottom;
355 
356  auto& renderer = parent_.get_manager().get_renderer();
357 
358  if (background_texture_) {
359  const vector2f canvas_tl = background_texture_->get_canvas_uv(vector2f(0.0f, 0.0f), true);
360  const vector2f canvas_br = background_texture_->get_canvas_uv(vector2f(1.0f, 1.0f), true);
361  const bounds2f canvas_uvs = bounds2f(canvas_tl.x, canvas_br.x, canvas_tl.y, canvas_br.y);
362 
363  float rounded_tile_size =
365 
366  if (background_texture_->is_in_atlas() && is_background_tilling_ &&
367  rounded_tile_size > 1.0f) {
368  repeat_wrap(
369  parent_, background_quads_, canvas_uvs, rounded_tile_size, false, c, borders);
370  } else {
371  background_quads_.emplace_back();
372  auto& quad = background_quads_.back();
373 
374  quad[0].pos = parent_.round_to_pixel(borders.top_left());
375  quad[1].pos = parent_.round_to_pixel(borders.top_right());
376  quad[2].pos = parent_.round_to_pixel(borders.bottom_right());
377  quad[3].pos = parent_.round_to_pixel(borders.bottom_left());
378  quad[0].uvs = canvas_uvs.top_left();
379  quad[1].uvs = canvas_uvs.top_right();
380  quad[2].uvs = canvas_uvs.bottom_right();
381  quad[3].uvs = canvas_uvs.bottom_left();
382  quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
383  }
384  } else {
385  background_quads_.emplace_back();
386  auto& quad = background_quads_.back();
387 
388  quad[0].pos = parent_.round_to_pixel(borders.top_left());
389  quad[1].pos = parent_.round_to_pixel(borders.top_right());
390  quad[2].pos = parent_.round_to_pixel(borders.bottom_right());
391  quad[3].pos = parent_.round_to_pixel(borders.bottom_left());
392  quad[0].uvs = vector2f(0.0f, 0.0f);
393  quad[1].uvs = vector2f(0.0f, 0.0f);
394  quad[2].uvs = vector2f(0.0f, 0.0f);
395  quad[3].uvs = vector2f(0.0f, 0.0f);
396 
397  quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
398  }
399 
400  if (renderer.is_vertex_cache_enabled() && !renderer.is_quad_batching_enabled()) {
401  if (!background_cache_)
402  background_cache_ = renderer.create_vertex_cache(vertex_cache::type::quads);
403 
404  background_cache_->update(background_quads_[0].data(), background_quads_.size() * 4);
405  background_quads_.clear();
406  }
407 }
408 
409 void backdrop::update_edge_(color c) const {
410  if (!edge_texture_ && edge_color_ == color::empty)
411  return;
412 
413  if (!edge_texture_)
414  c *= edge_color_;
415 
416  constexpr float uv_step = 1.0f / 8.0f;
417  auto borders = parent_.get_borders();
418  borders.left += edge_insets_.left;
419  borders.right -= edge_insets_.right;
420  borders.top += edge_insets_.top;
421  borders.bottom -= edge_insets_.bottom;
422 
423  auto& renderer = parent_.get_manager().get_renderer();
424  const float rounded_edge_size =
426 
427  auto repeat_wrap_edge = [&](const bounds2f& source_uvs, bool is_rotated,
428  const bounds2f& destination) {
429  if (edge_texture_) {
430  const vector2f canvas_tl = edge_texture_->get_canvas_uv(source_uvs.top_left(), true);
431  const vector2f canvas_br =
432  edge_texture_->get_canvas_uv(source_uvs.bottom_right(), true);
433  const bounds2f canvas_uvs =
434  bounds2f(canvas_tl.x, canvas_br.x, canvas_tl.y, canvas_br.y);
435 
436  if (edge_texture_->is_in_atlas() && rounded_edge_size > 1.0f) {
437  repeat_wrap(
438  parent_, edge_quads_, canvas_uvs, rounded_edge_size, is_rotated, c,
439  destination);
440  } else {
441  edge_quads_.emplace_back();
442  auto& quad = edge_quads_.back();
443 
444  quad[0].pos = parent_.round_to_pixel(destination.top_left());
445  quad[1].pos = parent_.round_to_pixel(destination.top_right());
446  quad[2].pos = parent_.round_to_pixel(destination.bottom_right());
447  quad[3].pos = parent_.round_to_pixel(destination.bottom_left());
448 
449  if (rounded_edge_size <= 1.0f) {
450  quad[0].uvs = canvas_uvs.top_left();
451  quad[1].uvs = canvas_uvs.top_right();
452  quad[2].uvs = canvas_uvs.bottom_right();
453  quad[3].uvs = canvas_uvs.bottom_left();
454  } else {
455  if (is_rotated) {
456  float factor = destination.width() / rounded_edge_size;
457  quad[0].uvs =
458  canvas_uvs.top_left() + vector2f(0.0, factor * canvas_uvs.height());
459  quad[1].uvs = canvas_uvs.top_left();
460  quad[2].uvs = canvas_uvs.top_right();
461  quad[3].uvs =
462  canvas_uvs.top_right() + vector2f(0.0, factor * canvas_uvs.height());
463  } else {
464  float factor = destination.height() / rounded_edge_size;
465  quad[0].uvs = canvas_uvs.top_left();
466  quad[1].uvs = canvas_uvs.top_right();
467  quad[2].uvs =
468  canvas_uvs.top_right() + vector2f(0.0, factor * canvas_uvs.height());
469  quad[3].uvs =
470  canvas_uvs.top_left() + vector2f(0.0, factor * canvas_uvs.height());
471  }
472  }
473 
474  quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
475  }
476  } else {
477  edge_quads_.emplace_back();
478  auto& quad = edge_quads_.back();
479 
480  quad[0].pos = parent_.round_to_pixel(destination.top_left());
481  quad[1].pos = parent_.round_to_pixel(destination.top_right());
482  quad[2].pos = parent_.round_to_pixel(destination.bottom_right());
483  quad[3].pos = parent_.round_to_pixel(destination.bottom_left());
484  quad[0].uvs = vector2f(0.0f, 0.0f);
485  quad[1].uvs = vector2f(0.0f, 0.0f);
486  quad[2].uvs = vector2f(0.0f, 0.0f);
487  quad[3].uvs = vector2f(0.0f, 0.0f);
488  quad[0].col = quad[1].col = quad[2].col = quad[3].col = c;
489  }
490  };
491 
492  // Left edge
493  repeat_wrap_edge(
494  bounds2f(0.0f, uv_step, 0.0f, 1.0f), false,
495  bounds2f(
496  borders.left, borders.left + rounded_edge_size, borders.top + rounded_edge_size,
497  borders.bottom - rounded_edge_size));
498 
499  // Right edge
500  repeat_wrap_edge(
501  bounds2f(uv_step, 2.0f * uv_step, 0.0f, 1.0f), false,
502  bounds2f(
503  borders.right - rounded_edge_size, borders.right, borders.top + rounded_edge_size,
504  borders.bottom - rounded_edge_size));
505 
506  // Top edge
507  repeat_wrap_edge(
508  bounds2f(2.0f * uv_step, 3.0f * uv_step, 0.0f, 1.0f), true,
509  bounds2f(
510  borders.left + rounded_edge_size, borders.right - rounded_edge_size, borders.top,
511  borders.top + rounded_edge_size));
512 
513  // Bottom edge
514  repeat_wrap_edge(
515  bounds2f(3.0f * uv_step, 4.0f * uv_step, 0.0f, 1.0f), true,
516  bounds2f(
517  borders.left + rounded_edge_size, borders.right - rounded_edge_size,
518  borders.bottom - rounded_edge_size, borders.bottom));
519 
520  // Top-left corner
521  repeat_wrap_edge(
522  bounds2f(4.0f * uv_step, 5.0f * uv_step, 0.0f, 1.0f), false,
523  bounds2f(
524  borders.left, borders.left + rounded_edge_size, borders.top,
525  borders.top + rounded_edge_size));
526 
527  // Top-right corner
528  repeat_wrap_edge(
529  bounds2f(5.0f * uv_step, 6.0f * uv_step, 0.0f, 1.0f), false,
530  bounds2f(
531  borders.right - rounded_edge_size, borders.right, borders.top,
532  borders.top + rounded_edge_size));
533 
534  // Bottom-left corner
535  repeat_wrap_edge(
536  bounds2f(6.0f * uv_step, 7.0f * uv_step, 0.0f, 1.0f), false,
537  bounds2f(
538  borders.left, borders.left + rounded_edge_size, borders.bottom - rounded_edge_size,
539  borders.bottom));
540 
541  // Bottom-right corner
542  repeat_wrap_edge(
543  bounds2f(7.0f * uv_step, 8.0f * uv_step, 0.0f, 1.0f), false,
544  bounds2f(
545  borders.right - rounded_edge_size, borders.right, borders.bottom - rounded_edge_size,
546  borders.bottom));
547 
548  if (renderer.is_vertex_cache_enabled() && !renderer.is_quad_batching_enabled()) {
549  if (!edge_cache_)
550  edge_cache_ = renderer.create_vertex_cache(vertex_cache::type::quads);
551 
552  edge_cache_->update(edge_quads_[0].data(), edge_quads_.size() * 4);
553  edge_quads_.clear();
554  }
555 }
556 
557 } // 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:12
static const color empty
Definition: gui_color.hpp:42
A region that can contain other regions and react to events.
Definition: gui_frame.hpp:255
const renderer & get_renderer() const
Returns the renderer implementation.
const bounds2f & get_borders() const
Returns this region's borders.
Definition: gui_region.cpp:361
float get_effective_alpha() const
Returns this region's effective alpha (opacity).
Definition: gui_region.cpp:161
manager & get_manager()
Returns this region's manager.
Definition: gui_region.hpp:693
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.
Definition: gui_region.cpp:573
const std::string & get_name() const
Returns this region's name.
Definition: gui_region.cpp:140
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).
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)
std::ostream out
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
Definition: gui_bounds2.hpp:56
T width() const noexcept
Definition: gui_bounds2.hpp:52
vector2< T > top_left() const noexcept
Definition: gui_bounds2.hpp:32
Simple structure holding four vertices and a material.
Definition: gui_quad.hpp:18