The Battle for Wesnoth  1.19.1+dev
canvas.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 - 2024
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Implementation of canvas.hpp.
19  */
20 
21 #define GETTEXT_DOMAIN "wesnoth-lib"
22 
23 #include "gui/core/canvas.hpp"
25 
26 #include "draw.hpp"
27 #include "draw_manager.hpp"
28 #include "font/text.hpp"
29 #include "formatter.hpp"
30 #include "gettext.hpp"
32 #include "gui/core/log.hpp"
33 #include "gui/widgets/helper.hpp"
34 #include "font/standard_colors.hpp"
35 #include "picture.hpp"
36 #include "sdl/point.hpp"
37 #include "sdl/rect.hpp"
38 #include "sdl/texture.hpp"
39 #include "sdl/utils.hpp" // blur_surface
40 #include "video.hpp" // read_pixels_low_res, only used for blurring
41 #include "wml_exception.hpp"
42 
43 #include <iostream>
44 
45 namespace gui2
46 {
47 
48 /***** ***** ***** ***** ***** LINE ***** ***** ***** ***** *****/
49 
51  : shape(cfg)
52  , x1_(cfg["x1"])
53  , y1_(cfg["y1"])
54  , x2_(cfg["x2"])
55  , y2_(cfg["y2"])
56  , color_(cfg["color"])
57  , thickness_(cfg["thickness"])
58 {
59  const std::string& debug = (cfg["debug"]);
60  if(!debug.empty()) {
61  DBG_GUI_P << "Line: found debug message '" << debug << "'.";
62  }
63 }
64 
66 {
67  /**
68  * @todo formulas are now recalculated every draw cycle which is a bit silly
69  * unless there has been a resize. So to optimize we should use an extra
70  * flag or do the calculation in a separate routine.
71  */
72 
73  const unsigned x1 = x1_(variables);
74  const unsigned y1 = y1_(variables);
75  const unsigned x2 = x2_(variables);
76  const unsigned y2 = y2_(variables);
77 
78  DBG_GUI_D << "Line: draw from " << x1 << ',' << y1 << " to " << x2 << ',' << y2 << ".";
79 
80  // @todo FIXME respect the thickness.
81 
82  draw::line(x1, y1, x2, y2, color_(variables));
83 }
84 
85 /***** ***** ***** ***** ***** Rectangle ***** ***** ***** ***** *****/
86 
88  : rect_bounded_shape(cfg)
89  , border_thickness_(cfg["border_thickness"])
90  , border_color_(cfg["border_color"], color_t::null_color())
91  , fill_color_(cfg["fill_color"], color_t::null_color())
92 {
93  // Check if a raw color string evaluates to a null color.
94  if(!border_color_.has_formula() && border_color_().null()) {
96  }
97 
98  const std::string& debug = (cfg["debug"]);
99  if(!debug.empty()) {
100  DBG_GUI_P << "Rectangle: found debug message '" << debug << "'.";
101  }
102 }
103 
105 {
106  const int x = x_(variables);
107  const int y = y_(variables);
108  const int w = w_(variables);
109  const int h = h_(variables);
110 
111  const color_t fill_color = fill_color_(variables);
112 
113  // Fill the background, if applicable
114  if(!fill_color.null()) {
115  DBG_GUI_D << "fill " << fill_color;
116  draw::set_color(fill_color);
117 
118  const SDL_Rect area {
119  x + border_thickness_,
120  y + border_thickness_,
121  w - (border_thickness_ * 2),
122  h - (border_thickness_ * 2)
123  };
124 
125  draw::fill(area);
126  }
127 
128  const color_t border_color = border_color_(variables);
129 
130  // Draw the border
131  draw::set_color(border_color);
132  DBG_GUI_D << "border thickness " << border_thickness_
133  << ", colour " << border_color;
134  for(int i = 0; i < border_thickness_; ++i) {
135  const SDL_Rect dimensions {
136  x + i,
137  y + i,
138  w - (i * 2),
139  h - (i * 2)
140  };
141 
142  draw::rect(dimensions);
143  }
144 }
145 
146 /***** ***** ***** ***** ***** Rounded Rectangle ***** ***** ***** ***** *****/
147 
149  : rect_bounded_shape(cfg)
150  , r_(cfg["corner_radius"])
151  , border_thickness_(cfg["border_thickness"])
152  , border_color_(cfg["border_color"], color_t::null_color())
153  , fill_color_(cfg["fill_color"], color_t::null_color())
154 {
155  // Check if a raw color string evaluates to a null color.
156  if(!border_color_.has_formula() && border_color_().null()) {
157  border_thickness_ = 0;
158  }
159 
160  const std::string& debug = (cfg["debug"]);
161  if(!debug.empty()) {
162  DBG_GUI_P << "Rounded Rectangle: found debug message '" << debug << "'.";
163  }
164 }
165 
167 {
168  const int x = x_(variables);
169  const int y = y_(variables);
170  const int w = w_(variables);
171  const int h = h_(variables);
172  const int r = r_(variables);
173 
174  DBG_GUI_D << "Rounded Rectangle: draw from " << x << ',' << y << " width " << w << " height " << h << ".";
175 
176  const color_t fill_color = fill_color_(variables);
177 
178  // Fill the background, if applicable
179  if(!fill_color.null() && w && h) {
180  draw::set_color(fill_color);
181 
182  draw::fill(rect{x + r, y + border_thickness_, w - r * 2, r - border_thickness_ + 1});
183  draw::fill(rect{x + border_thickness_, y + r + 1, w - border_thickness_ * 2, h - r * 2});
184  draw::fill(rect{x + r, y - r + h + 1, w - r * 2, r - border_thickness_});
185 
186  draw::disc(x + r, y + r, r, 0xc0);
187  draw::disc(x + w - r, y + r, r, 0x03);
188  draw::disc(x + r, y + h - r, r, 0x30);
189  draw::disc(x + w - r, y + h - r, r, 0x0c);
190  }
191 
192  const color_t border_color = border_color_(variables);
193 
194  // Draw the border
195  draw::set_color(border_color);
196 
197  for(int i = 0; i < border_thickness_; ++i) {
198  draw::line(x + r, y + i, x + w - r, y + i);
199  draw::line(x + r, y + h - i, x + w - r, y + h - i);
200 
201  draw::line(x + i, y + r, x + i, y + h - r);
202  draw::line(x + w - i, y + r, x + w - i, y + h - r);
203 
204  draw::circle(x + r, y + r, r - i, 0xc0);
205  draw::circle(x + w - r, y + r, r - i, 0x03);
206  draw::circle(x + r, y + h - r, r - i, 0x30);
207  draw::circle(x + w - r, y + h - r, r - i, 0x0c);
208  }
209 }
210 
211 /***** ***** ***** ***** ***** CIRCLE ***** ***** ***** ***** *****/
212 
214  : shape(cfg)
215  , x_(cfg["x"])
216  , y_(cfg["y"])
217  , radius_(cfg["radius"])
218  , border_color_(cfg["border_color"])
219  , fill_color_(cfg["fill_color"])
220  , border_thickness_(cfg["border_thickness"].to_int(1))
221 {
222  const std::string& debug = (cfg["debug"]);
223  if(!debug.empty()) {
224  DBG_GUI_P << "Circle: found debug message '" << debug << "'.";
225  }
226 }
227 
229 {
230  /**
231  * @todo formulas are now recalculated every draw cycle which is a bit
232  * silly unless there has been a resize. So to optimize we should use an
233  * extra flag or do the calculation in a separate routine.
234  */
235 
236  const int x = x_(variables);
237  const int y = y_(variables);
238  const unsigned radius = radius_(variables);
239 
240  DBG_GUI_D << "Circle: drawn at " << x << ',' << y << " radius " << radius << ".";
241 
242  const color_t fill_color = fill_color_(variables);
243  if(!fill_color.null() && radius) {
244  draw::disc(x, y, radius, fill_color);
245  }
246 
247  const color_t border_color = border_color_(variables);
248  for(unsigned int i = 0; i < border_thickness_; i++) {
249  draw::circle(x, y, radius - i, border_color);
250  }
251 }
252 
253 /***** ***** ***** ***** ***** IMAGE ***** ***** ***** ***** *****/
254 
256  : shape(cfg)
257  , x_(cfg["x"])
258  , y_(cfg["y"])
259  , w_(cfg["w"])
260  , h_(cfg["h"])
261  , image_name_(cfg["name"])
262  , resize_mode_(get_resize_mode(cfg["resize_mode"]))
263  , mirror_(cfg.get_old_attribute("mirror", "vertical_mirror", "image"))
264  , actions_formula_(cfg["actions"], &functions)
265 {
266  const std::string& debug = (cfg["debug"]);
267  if(!debug.empty()) {
268  DBG_GUI_P << "Image: found debug message '" << debug << "'.";
269  }
270 }
271 
272 void image_shape::dimension_validation(unsigned value, const std::string& name, const std::string& key)
273 {
274  const int as_int = static_cast<int>(value);
275 
276  VALIDATE_WITH_DEV_MESSAGE(as_int >= 0, _("Image doesn't fit on canvas."),
277  formatter() << "Image '" << name << "', " << key << " = " << as_int << "."
278  );
279 }
280 
282 {
283  DBG_GUI_D << "Image: draw.";
284 
285  /**
286  * @todo formulas are now recalculated every draw cycle which is a bit
287  * silly unless there has been a resize. So to optimize we should use an
288  * extra flag or do the calculation in a separate routine.
289  */
290  const std::string& name = image_name_(variables);
291 
292  if(name.empty()) {
293  DBG_GUI_D << "Image: formula returned no value, will not be drawn.";
294  return;
295  }
296 
297  // Texture filtering mode must be set on texture creation,
298  // so check whether we need smooth scaling or not here.
302  {
304  }
306 
307  if(!tex) {
308  ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn.";
309  return;
310  }
311 
312  wfl::map_formula_callable local_variables(variables);
313  local_variables.add("image_original_width", wfl::variant(tex.w()));
314  local_variables.add("image_original_height", wfl::variant(tex.h()));
315 
316  int w = w_(local_variables);
317  dimension_validation(w, name, "w");
318 
319  int h = h_(local_variables);
320  dimension_validation(h, name, "h");
321 
322  local_variables.add("image_width", wfl::variant(w ? w : tex.w()));
323  local_variables.add("image_height", wfl::variant(h ? h : tex.h()));
324 
325  const int x = x_(local_variables);
326  const int y = y_(local_variables);
327 
328  // used in gui/dialogs/story_viewer.cpp
329  local_variables.add("clip_x", wfl::variant(x));
330  local_variables.add("clip_y", wfl::variant(y));
331 
332  if (variables.has_key("fake_draw") && variables.query_value("fake_draw").as_bool()) {
333  variables.add("image_original_width", wfl::variant(tex.w()));
334  variables.add("image_original_height", wfl::variant(tex.h()));
335  variables.add("image_width", wfl::variant(w ? w : tex.w()));
336  variables.add("image_height", wfl::variant(h ? h : tex.h()));
337  return;
338  }
339 
340  // Execute the provided actions for this context.
341  wfl::variant(variables.fake_ptr()).execute_variant(actions_formula_.evaluate(local_variables));
342 
343  // Useful for relative, grid like positioning
344  variables.add("item_x", wfl::variant(x));
345  variables.add("item_y", wfl::variant(y));
346  variables.add("item_width", wfl::variant(w ? w : tex.w()));
347  variables.add("item_height", wfl::variant(h ? h : tex.h()));
348 
349  // If w or h is 0, assume it means the whole image.
350  if (!w) { w = tex.w(); }
351  if (!h) { h = tex.h(); }
352 
353  const SDL_Rect dst_rect { x, y, w, h };
354 
355  // What to do with the image depends on whether we need to tile it or not.
356  switch(resize_mode_) {
357  case resize_mode::tile:
358  draw::tiled(tex, dst_rect, false, mirror_(variables));
359  break;
361  draw::tiled(tex, dst_rect, true, mirror_(variables));
362  break;
364  draw::tiled_highres(tex, dst_rect, false, mirror_(variables));
365  break;
367  // Stretching is identical to scaling in terms of handling.
368  // Is this intended? That's what previous code was doing.
369  case resize_mode::scale:
370  // Filtering mode is set on texture creation above.
371  // Handling is otherwise identical to sharp scaling.
373  if(mirror_(variables)) {
374  draw::flipped(tex, dst_rect);
375  } else {
376  draw::blit(tex, dst_rect);
377  }
378  break;
379  default:
380  ERR_GUI_D << "Image: unrecognized resize mode.";
381  break;
382  }
383 }
384 
386 {
387  if(resize_mode == "tile") {
388  return resize_mode::tile;
389  } else if(resize_mode == "tile_center") {
391  } else if(resize_mode == "tile_highres") {
393  } else if(resize_mode == "stretch") {
394  return resize_mode::stretch;
395  } else if(resize_mode == "scale_sharp") {
397  } else if(resize_mode == "scale") {
398  return resize_mode::scale;
399  } else {
400  if(!resize_mode.empty()) {
401  ERR_GUI_E << "Invalid resize mode '" << resize_mode << "' falling back to 'scale'.";
402  }
403 
404  // Linear scaling just looks horrible as a default, especially on HDPI screens, and even
405  // for some non-pixel art (the logo, for example). Nearest-neighbor isn't perfect for those
406  // usecases, but it's definitely better, in my opinion.
407  //
408  // -- vultraz, 2022-08-20
410  }
411 }
412 
413 /***** ***** ***** ***** ***** TEXT ***** ***** ***** ***** *****/
414 
416  : rect_bounded_shape(cfg)
417  , font_family_(font::str_to_family_class(cfg["font_family"]))
418  , font_size_(cfg["font_size"])
419  , font_style_(decode_font_style(cfg["font_style"]))
420  , text_alignment_(cfg["text_alignment"])
421  , color_(cfg["color"])
422  , text_(cfg["text"])
423  , text_markup_(cfg["text_markup"], false)
424  , link_aware_(cfg["text_link_aware"], false)
425  , link_color_(cfg["text_link_color"], color_t::from_hex_string("ffff00"))
426  , maximum_width_(cfg["maximum_width"], -1)
427  , characters_per_line_(cfg["text_characters_per_line"])
428  , maximum_height_(cfg["maximum_height"], -1)
429  , highlight_start_(cfg["highlight_start"])
430  , highlight_end_(cfg["highlight_end"])
431  , highlight_color_(cfg["highlight_color"], color_t::from_hex_string("215380"))
432  , attr_start_(cfg["attr_start"])
433  , attr_end_(cfg["attr_end"])
434  , attr_name_(cfg["attr_name"])
435  , attr_data_(cfg["attr_data"])
436  , outline_(cfg["outline"], false)
437  , actions_formula_(cfg["actions"], &functions)
438 {
439 
440  if(!font_size_.has_formula()) {
441  VALIDATE(font_size_(), _("Text has a font size of 0."));
442  }
443 
444  const std::string& debug = (cfg["debug"]);
445  if(!debug.empty()) {
446  DBG_GUI_P << "Text: found debug message '" << debug << "'.";
447  }
448 }
449 
451 {
452  assert(variables.has_key("text"));
453 
454  // We first need to determine the size of the text which need the rendered
455  // text. So resolve and render the text first and then start to resolve
456  // the other formulas.
457  const t_string text = text_(variables);
458 
459  if(text.empty()) {
460  DBG_GUI_D << "Text: no text to render, leave.";
461  return;
462  }
463 
464  font::pango_text& text_renderer = font::get_text_renderer();
465  text_renderer.clear_attribute_list();
466 
467  std::vector<std::string> starts = utils::split(highlight_start_, ',');
468  std::vector<std::string> stops = utils::split(highlight_end_, ',');
469 
470  for(size_t i = 0; i < std::min(starts.size(), stops.size()); i++) {
471  typed_formula<int> hstart(starts.at(i));
472  typed_formula<int> hstop(stops.at(i));
473  text_renderer.set_highlight_area(hstart(variables), hstop(variables), highlight_color_(variables));
474  }
475 
476  // TODO check the strings before parsing them
477  starts = utils::split(attr_start_, ',');
478  stops = utils::split(attr_end_, ',');
479  std::vector<std::string> styles = utils::split(attr_name_, ',');
480  std::vector<std::string> data = utils::split(attr_data_, ',');
481 
482  for(size_t i = 0, data_index = 0; i < std::min(starts.size(), stops.size()); i++) {
483  if (styles.at(i).empty()) {
484  continue;
485  }
486 
487  typed_formula<int> attr_start(starts.at(i));
488  typed_formula<int> attr_stop(stops.at(i));
489 
490  // Note that the value corresponding to the attribute is not the i-th item
491  // but rather the data_index-th one. Using data_index so that we can get rid of excess commas
492  // that is, things like 'attr_data=value1,,,valu2,value3'
493  if (styles.at(i) == "color"||styles.at(i) == "fgcolor"||styles.at(i) == "foreground") {
494  text_renderer.add_attribute_fg_color(attr_start(variables), attr_stop(variables), font::string_to_color(data.at(data_index)));
495  data_index++;
496  } else if (styles.at(i) == "bgcolor"||styles.at(i) == "background") {
497  text_renderer.set_highlight_area(attr_start(variables), attr_stop(variables), font::string_to_color(data.at(data_index)));
498  data_index++;
499  } else if (styles.at(i) == "font_size"||styles.at(i) == "size") {
500  text_renderer.add_attribute_size(attr_start(variables), attr_stop(variables), std::stoi(data.at(data_index)));
501  data_index++;
502  } else if (styles.at(i) == "font_family"||styles.at(i) == "face") {
503  text_renderer.add_attribute_font_family(attr_start(variables), attr_stop(variables), data.at(data_index));
504  data_index++;
505  } else if (styles.at(i) == "weight") {
506  text_renderer.add_attribute_weight(attr_start(variables), attr_stop(variables), decode_text_weight(data.at(data_index)));
507  data_index++;
508  } else if (styles.at(i) == "style") {
509  text_renderer.add_attribute_style(attr_start(variables), attr_stop(variables), decode_text_style(data.at(data_index)));
510  data_index++;
511  } else {
512  font::pango_text::FONT_STYLE attr_style = decode_font_style(styles.at(i));
513  switch(attr_style)
514  {
516  text_renderer.add_attribute_weight(attr_start(variables), attr_stop(variables), PANGO_WEIGHT_BOLD);
517  break;
519  text_renderer.add_attribute_style(attr_start(variables), attr_stop(variables), PANGO_STYLE_ITALIC);
520  break;
522  text_renderer.add_attribute_underline(attr_start(variables), attr_stop(variables), PANGO_UNDERLINE_SINGLE);
523  break;
524  default:
525  // Unsupported formatting or normal text
526  text_renderer.add_attribute_weight(attr_start(variables), attr_stop(variables), PANGO_WEIGHT_NORMAL);
527  text_renderer.add_attribute_style(attr_start(variables), attr_stop(variables), PANGO_STYLE_NORMAL);
528  }
529  }
530  }
531 
532  text_renderer
533  .set_link_aware(link_aware_(variables))
534  .set_link_color(link_color_(variables))
535  .set_text(text, text_markup_(variables));
536 
537  text_renderer.set_family_class(font_family_)
538  .set_font_size(font_size_(variables))
540  .set_alignment(text_alignment_(variables))
541  .set_foreground_color(color_(variables))
542  .set_maximum_width(maximum_width_(variables))
543  .set_maximum_height(maximum_height_(variables), true)
544  .set_ellipse_mode(variables.has_key("text_wrap_mode")
545  ? static_cast<PangoEllipsizeMode>(variables.query_value("text_wrap_mode").as_int())
546  : PANGO_ELLIPSIZE_END)
548  .set_add_outline(outline_(variables));
549 
550  wfl::map_formula_callable local_variables(variables);
551  const auto [tw, th] = text_renderer.get_size();
552 
553  // Translate text width and height back to draw-space, rounding up.
554  local_variables.add("text_width", wfl::variant(tw));
555  local_variables.add("text_height", wfl::variant(th));
556 
557  if (variables.has_key("fake_draw") && variables.query_value("fake_draw").as_bool()) {
558  variables.add("text_width", wfl::variant(tw));
559  variables.add("text_height", wfl::variant(th));
560  return;
561  }
562 
563  const int x = x_(local_variables);
564  const int y = y_(local_variables);
565  const int w = w_(local_variables);
566  const int h = h_(local_variables);
567  rect dst_rect{x, y, w, h};
568 
569  // Execute the provided actions for this context.
570  wfl::variant(variables.fake_ptr()).execute_variant(actions_formula_.evaluate(local_variables));
571 
572  // Useful for relative, grid like positioning
573  variables.add("item_x", wfl::variant(x));
574  variables.add("item_y", wfl::variant(y));
575  variables.add("item_width", wfl::variant(tw));
576  variables.add("item_height", wfl::variant(th));
577 
578  texture tex = text_renderer.render_and_get_texture();
579  if(!tex) {
580  DBG_GUI_D << "Text: Rendering '" << text << "' resulted in an empty canvas, leave.";
581  return;
582  }
583 
584  dst_rect.w = std::min(dst_rect.w, tex.w());
585  dst_rect.h = std::min(dst_rect.h, tex.h());
586 
587  draw::blit(tex, dst_rect);
588 }
589 
590 /***** ***** ***** ***** ***** CANVAS ***** ***** ***** ***** *****/
591 
593  : shapes_()
594  , blur_depth_(0)
595  , blur_region_(sdl::empty_rect)
596  , deferred_(false)
597  , w_(0)
598  , h_(0)
599  , variables_()
600  , functions_()
601 {
602 }
603 
605  : shapes_(std::move(c.shapes_))
606  , blur_depth_(c.blur_depth_)
607  , blur_region_(c.blur_region_)
608  , deferred_(c.deferred_)
609  , w_(c.w_)
610  , h_(c.h_)
611  , variables_(c.variables_)
612  , functions_(c.functions_)
613 {
614 }
615 
616 // It would be better if the blur effect was managed at a higher level.
617 // But for now this works and should be both general and robust.
618 bool canvas::update_blur(const rect& screen_region, bool force)
619 {
620  if(!blur_depth_) {
621  // No blurring needed.
622  return true;
623  }
624 
625  if(screen_region != blur_region_) {
626  DBG_GUI_D << "blur region changed from " << blur_region_
627  << " to " << screen_region;
628  // something has changed. regenerate the texture.
630  blur_region_ = screen_region;
631  }
632 
633  if(blur_texture_ && !force) {
634  // We already made the blur. It's expensive, so don't do it again.
635  return true;
636  }
637 
638  // To blur what is underneath us, it must already be rendered somewhere.
639  // This is okay for sub-elements of an opaque window (panels on the main
640  // title screen for example) as the window will already have rendered
641  // its background to the render buffer before we get here.
642  // If however we are blurring elements behind the window, such as if
643  // the window itself is translucent (objectives popup), or it is
644  // transparent with a translucent element (character dialogue),
645  // then we need to render what will be behind it before capturing that
646  // and rendering a blur.
647  // We could use the previous render frame, but there could well have been
648  // another element there last frame such as a popup window which we
649  // don't want to be part of the blur.
650  // The stable solution is to render in multiple passes,
651  // so that is what we shall do.
652 
653  // For the first pass, this element and its children are not rendered.
654  if(!deferred_) {
655  DBG_GUI_D << "Deferring blur at " << screen_region;
656  deferred_ = true;
658  return false;
659  }
660 
661  // For the second pass we read the result of the first pass at
662  // this widget's location, and blur it.
663  DBG_GUI_D << "Blurring " << screen_region << " depth " << blur_depth_;
664  rect read_region = screen_region;
665  auto setter = draw::set_render_target({});
666  surface s = video::read_pixels_low_res(&read_region);
669  deferred_ = false;
670  return true;
671 }
672 
674 {
676 }
677 
679 {
680  // This early-return has to come before the `validate(rect.w <= w_)` check, as during the boost_unit_tests execution
681  // the debug_clock widget will have no shapes, 0x0 size, yet be given a larger rect to draw.
682  if(shapes_.empty()) {
683  DBG_GUI_D << "Canvas: empty (no shapes to draw).";
684  return;
685  }
686 
687  if(deferred_) {
688  // We will draw next frame.
689  return;
690  }
691 
692  // Draw blurred background.
693  // TODO: hwaccel - this should be able to be removed at some point with shaders
694  if(blur_depth_ && blur_texture_) {
695  DBG_GUI_D << "blitting blur size " << blur_texture_.draw_size();
697  }
698 
699  // Draw items
700  for(auto& shape : shapes_) {
701  const lg::scope_logger inner_scope_logging_object__{log_gui_draw, "Canvas: draw shape."};
703  }
704 }
705 
706 void canvas::parse_cfg(const config& cfg)
707 {
708  log_scope2(log_gui_parse, "Canvas: parsing config.");
709 
710  for(const auto shape : cfg.all_children_range())
711  {
712  const std::string& type = shape.key;
713  const config& data = shape.cfg;
714 
715  DBG_GUI_P << "Canvas: found shape of the type " << type << ".";
716 
717  if(type == "line") {
718  shapes_.emplace_back(std::make_unique<line_shape>(data));
719  } else if(type == "rectangle") {
720  shapes_.emplace_back(std::make_unique<rectangle_shape>(data));
721  } else if(type == "round_rectangle") {
722  shapes_.emplace_back(std::make_unique<round_rectangle_shape>(data));
723  } else if(type == "circle") {
724  shapes_.emplace_back(std::make_unique<circle_shape>(data));
725  } else if(type == "image") {
726  shapes_.emplace_back(std::make_unique<image_shape>(data, functions_));
727  } else if(type == "text") {
728  shapes_.emplace_back(std::make_unique<text_shape>(data, functions_));
729  } else if(type == "pre_commit") {
730 
731  /* note this should get split if more preprocessing is used. */
732  for(const auto function : data.all_children_range())
733  {
734 
735  if(function.key == "blur") {
736  blur_depth_ = function.cfg["depth"];
737  } else {
738  ERR_GUI_P << "Canvas: found a pre commit function"
739  << " of an invalid type " << type << ".";
740  }
741  }
742 
743  } else {
744  ERR_GUI_P << "Canvas: found a shape of an invalid type " << type
745  << ".";
746  }
747  }
748 }
749 
751 {
753  variables_.add("width", wfl::variant(w_));
754  variables_.add("height", wfl::variant(h_));
755 }
756 
758 {
759  w_ = size.x;
760  h_ = size.y;
762 }
763 
764 void canvas::clear_shapes(const bool force)
765 {
766  if(force) {
767  shapes_.clear();
768  } else {
769  auto conditional = [](const std::unique_ptr<shape>& s)->bool { return !s->immutable(); };
770 
771  auto iter = std::remove_if(shapes_.begin(), shapes_.end(), conditional);
772  shapes_.erase(iter, shapes_.end());
773  }
774 }
775 
776 /***** ***** ***** ***** ***** SHAPE ***** ***** ***** ***** *****/
777 
778 } // namespace gui2
std::size_t w_
#define debug(x)
This file contains the canvas object which is the part where the widgets draw (temporally) images on.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
Text class.
Definition: text.hpp:79
pango_text & set_font_style(const FONT_STYLE font_style)
Definition: text.cpp:597
void set_highlight_area(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Mark a specific portion of text for highlighting.
Definition: text.cpp:497
void add_attribute_weight(const unsigned start_offset, const unsigned end_offset, PangoWeight weight)
Definition: text.cpp:375
point get_size()
Returns the size of the text, in drawing coordinates.
Definition: text.cpp:164
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:632
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:607
void add_attribute_font_family(const unsigned start_offset, const unsigned end_offset, std::string family)
Definition: text.cpp:473
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:575
void add_attribute_fg_color(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Definition: text.cpp:445
void add_attribute_size(const unsigned start_offset, const unsigned end_offset, int size)
Definition: text.cpp:352
pango_text & set_add_outline(bool do_add)
Definition: text.cpp:730
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:668
void add_attribute_underline(const unsigned start_offset, const unsigned end_offset, PangoUnderline underline)
Definition: text.cpp:421
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:688
pango_text & set_font_size(unsigned font_size)
Definition: text.cpp:585
pango_text & set_link_aware(bool b)
Definition: text.cpp:711
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:530
void add_attribute_style(const unsigned start_offset, const unsigned end_offset, PangoStyle style)
Definition: text.cpp:398
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:643
pango_text & set_maximum_width(int width)
Definition: text.cpp:616
texture render_and_get_texture()
Returns the cached texture, or creates a new one otherwise.
Definition: text.cpp:119
pango_text & set_link_color(const color_t &color)
Definition: text.cpp:720
void clear_attribute_list()
Clear all attributes.
Definition: text.cpp:525
std::ostringstream wrapper.
Definition: formatter.hpp:40
Abstract base class for all other shapes.
Definition: canvas.hpp:54
virtual void draw(wfl::map_formula_callable &variables)=0
Draws the canvas.
A simple canvas which can be drawn upon.
Definition: canvas.hpp:45
texture blur_texture_
Blurred background texture.
Definition: canvas.hpp:179
bool deferred_
Whether we have deferred rendering so we can capture for blur.
Definition: canvas.hpp:185
wfl::action_function_symbol_table functions_
Action function definitions for the canvas.
Definition: canvas.hpp:197
void clear_shapes(const bool force)
Definition: canvas.cpp:764
unsigned blur_depth_
The depth of the blur to use in the pre committing.
Definition: canvas.hpp:176
wfl::map_formula_callable variables_
The variables of the canvas.
Definition: canvas.hpp:194
bool update_blur(const rect &screen_region, const bool force=false)
Update the background blur texture, if relevant and necessary.
Definition: canvas.cpp:618
rect blur_region_
The region of the screen we have blurred (if any).
Definition: canvas.hpp:182
void parse_cfg(const config &cfg)
Parses a config object.
Definition: canvas.cpp:706
void queue_reblur()
Clear the cached blur texture, forcing it to regenerate.
Definition: canvas.cpp:673
std::vector< std::unique_ptr< shape > > shapes_
Vector with the shapes to draw.
Definition: canvas.hpp:166
unsigned w_
The full width of the canvas.
Definition: canvas.hpp:188
void update_size_variables()
Update WFL size variables.
Definition: canvas.cpp:750
unsigned h_
The full height of the canvas.
Definition: canvas.hpp:191
void draw()
Draw the canvas' shapes onto the screen.
Definition: canvas.cpp:678
void set_size(const point &size)
Definition: canvas.cpp:757
typed_formula< color_t > border_color_
The border color of the circle.
typed_formula< unsigned > x_
The center x coordinate of the circle.
typed_formula< unsigned > radius_
The radius of the circle.
unsigned int border_thickness_
The border thickness of the circle.
circle_shape(const config &cfg)
Constructor.
Definition: canvas.cpp:213
typed_formula< color_t > fill_color_
The fill color of the circle.
typed_formula< unsigned > y_
The center y coordinate of the circle.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:228
typed_formula< std::string > image_name_
Name of the image.
resize_mode
Determines the way an image will be resized.
typed_formula< unsigned > w_
The width of the image.
resize_mode get_resize_mode(const std::string &resize_mode)
Converts a string to a resize mode.
Definition: canvas.cpp:385
typed_formula< unsigned > x_
The x coordinate of the image.
static void dimension_validation(unsigned value, const std::string &name, const std::string &key)
Definition: canvas.cpp:272
typed_formula< unsigned > y_
The y coordinate of the image.
typed_formula< unsigned > h_
The height of the image.
resize_mode resize_mode_
The resize mode for an image.
wfl::formula actions_formula_
typed_formula< bool > mirror_
Mirror the image over the vertical axis.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:281
image_shape(const config &cfg, wfl::action_function_symbol_table &functions)
Constructor.
Definition: canvas.cpp:255
typed_formula< color_t > color_
The color of the line.
typed_formula< unsigned > x1_
The start x coordinate of the line.
typed_formula< unsigned > y1_
The start y coordinate of the line.
typed_formula< unsigned > x2_
The end x coordinate of the line.
line_shape(const config &cfg)
Constructor.
Definition: canvas.cpp:50
typed_formula< unsigned > y2_
The end y coordinate of the line.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:65
typed_formula< int > x_
The x coordinate of the rectangle.
typed_formula< int > w_
The width of the rectangle.
typed_formula< int > y_
The y coordinate of the rectangle.
typed_formula< int > h_
The height of the rectangle.
rectangle_shape(const config &cfg)
Constructor.
Definition: canvas.cpp:87
int border_thickness_
Border thickness.
typed_formula< color_t > fill_color_
The border color of the rectangle.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:104
typed_formula< color_t > border_color_
The border color of the rectangle.
typed_formula< color_t > border_color_
The border color of the rounded rectangle.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:166
typed_formula< int > r_
The radius of the corners.
round_rectangle_shape(const config &cfg)
Constructor.
Definition: canvas.cpp:148
int border_thickness_
Border thickness.
typed_formula< color_t > fill_color_
The border color of the rounded rectangle.
font::pango_text::FONT_STYLE font_style_
The style of the text.
std::string highlight_end_
std::string attr_name_
The attribute type Possible values : color/foreground, bgcolor/background, font_size/size,...
typed_formula< bool > outline_
Whether to apply a text outline.
typed_formula< color_t > color_
The color of the text.
typed_formula< int > maximum_height_
The maximum height for the text.
std::string attr_start_
Generic start and end offsets for various attributes.
std::string attr_data_
extra data for the attribute, if any
typed_formula< bool > link_aware_
The link aware switch of the text.
typed_formula< PangoAlignment > text_alignment_
The alignment of the text.
typed_formula< color_t > highlight_color_
The color to be used for highlighting.
std::string highlight_start_
Start and end offsets for highlight.
font::family_class font_family_
The text font family.
typed_formula< color_t > link_color_
The link color of the text.
typed_formula< int > maximum_width_
The maximum width for the text.
void draw(wfl::map_formula_callable &variables) override
Draws the canvas.
Definition: canvas.cpp:450
text_shape(const config &cfg, wfl::action_function_symbol_table &functions)
Constructor.
Definition: canvas.cpp:415
typed_formula< unsigned > font_size_
The font size of the text.
unsigned characters_per_line_
The number of characters per line.
wfl::formula actions_formula_
Any extra WFL actions to execute.
typed_formula< t_string > text_
The text to draw.
typed_formula< bool > text_markup_
The text markup switch of the text.
std::string attr_end_
bool has_formula() const
Determine whether the class contains a formula.
Generic locator abstracting the location of an image.
Definition: picture.hpp:63
bool empty() const
Definition: tstring.hpp:186
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:33
int w() const
The draw-space width of the texture, in pixels.
Definition: texture.hpp:105
void reset()
Releases ownership of the managed texture and resets the ptr to null.
Definition: texture.cpp:208
point draw_size() const
The size of the texture in draw-space.
Definition: texture.hpp:122
int h() const
The draw-space height of the texture, in pixels.
Definition: texture.hpp:114
formula_callable_ptr fake_ptr()
Definition: callable.hpp:42
variant query_value(const std::string &key) const
Definition: callable.hpp:50
bool has_key(const std::string &key) const
Definition: callable.hpp:82
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:40
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
variant execute_variant(const variant &to_exec)
Definition: variant.cpp:653
int as_int() const
Definition: variant.cpp:291
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
Drawing functions, for drawing things on the screen.
std::size_t i
Definition: function.cpp:968
int w
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define ERR_GUI_P
Definition: log.hpp:69
#define DBG_GUI_P
Definition: log.hpp:66
#define ERR_GUI_D
Definition: log.hpp:32
#define ERR_GUI_E
Definition: log.hpp:38
#define DBG_GUI_D
Definition: log.hpp:29
#define log_scope2(domain, description)
Definition: log.hpp:278
void request_extra_render_pass()
Request an extra render pass.
render_target_setter set_render_target(const texture &t)
Set the given texture as the active render target.
Definition: draw.cpp:661
void circle(int x, int y, int r, const color_t &c, uint8_t octants=0xff)
Draw a circle of the given colour.
Definition: draw.cpp:208
void tiled(const texture &tex, const SDL_Rect &dst, bool centered=false, bool mirrored=false)
Tile a texture to fill a region.
Definition: draw.cpp:369
void disc(int x, int y, int r, const color_t &c, uint8_t octants=0xff)
Draw a solid disc of the given colour.
Definition: draw.cpp:250
void set_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Set the drawing colour.
Definition: draw.cpp:100
void flipped(const texture &tex, const SDL_Rect &dst, bool flip_h=true, bool flip_v=false)
Draws a texture, or part of a texture, at the given location, also mirroring/flipping the texture hor...
Definition: draw.cpp:340
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:50
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:310
void tiled_highres(const texture &tex, const SDL_Rect &dst, bool centered=false, bool mirrored=false)
Tile a texture to fill a region.
Definition: draw.cpp:397
void rect(const SDL_Rect &rect)
Draw a rectangle.
Definition: draw.cpp:150
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
Collection of helper functions relating to Pango formatting.
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:1174
color_t string_to_color(const std::string &cmp_str)
Return the color the string represents.
family_class str_to_family_class(const std::string &str)
Generic file dialog.
void get_screen_size_variables(wfl::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:130
lg::log_domain log_gui_draw("gui/draw")
Definition: log.hpp:28
PangoWeight decode_text_weight(const std::string &weight)
Converts a text weight string to a PangoWeight.
Definition: helper.cpp:58
font::pango_text::FONT_STYLE decode_font_style(const std::string &style)
Converts a font style string to a font style.
Definition: helper.cpp:32
lg::log_domain log_gui_parse("gui/parse")
Definition: log.hpp:65
PangoStyle decode_text_style(const std::string &style)
Converts a text style string to a PangoStyle.
Definition: helper.cpp:79
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:960
scale_quality
Definition: picture.hpp:234
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:30
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::vector< std::string > split(const config_attribute_value &val)
surface read_pixels_low_res(SDL_Rect *r)
The same as read_pixels, but returns a low-resolution surface suitable for use with the old drawing s...
Definition: video.cpp:620
std::string_view data
Definition: picture.cpp:194
int x2_
Definition: pump.cpp:132
int y1_
Definition: pump.cpp:132
int x1_
Definition: pump.cpp:132
int y2_
Definition: pump.cpp:132
Contains the SDL_Rect helper code.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
constexpr bool null() const
Definition: color.hpp:186
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
mock_char c
static map_location::DIRECTION s
surface blur_surface(const surface &surf, int depth)
Cross-fades a surface.
Definition: utils.cpp:1167
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WITH_DEV_MESSAGE(cond, message, dev_message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define h