The Battle for Wesnoth  1.19.5+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/font_config.hpp"
35 #include "font/standard_colors.hpp"
36 #include "picture.hpp"
37 #include "sdl/point.hpp"
38 #include "sdl/rect.hpp"
39 #include "sdl/texture.hpp"
40 #include "sdl/utils.hpp" // blur_surface
41 #include "video.hpp" // read_pixels_low_res, only used for blurring
42 #include "wml_exception.hpp"
43 
44 #include <iostream>
45 
46 namespace gui2
47 {
48 
49 /***** ***** ***** ***** ***** LINE ***** ***** ***** ***** *****/
50 
52  : shape(cfg)
53  , x1_(cfg["x1"])
54  , y1_(cfg["y1"])
55  , x2_(cfg["x2"])
56  , y2_(cfg["y2"])
57  , color_(cfg["color"])
58  , thickness_(cfg["thickness"].to_unsigned())
59 {
60  const std::string& debug = (cfg["debug"]);
61  if(!debug.empty()) {
62  DBG_GUI_P << "Line: found debug message '" << debug << "'.";
63  }
64 }
65 
67 {
68  /**
69  * @todo formulas are now recalculated every draw cycle which is a bit silly
70  * unless there has been a resize. So to optimize we should use an extra
71  * flag or do the calculation in a separate routine.
72  */
73 
74  const unsigned x1 = x1_(variables);
75  const unsigned y1 = y1_(variables);
76  const unsigned x2 = x2_(variables);
77  const unsigned y2 = y2_(variables);
78 
79  DBG_GUI_D << "Line: draw from " << x1 << ',' << y1 << " to " << x2 << ',' << y2 << ".";
80 
81  // @todo FIXME respect the thickness.
82 
83  draw::line(x1, y1, x2, y2, color_(variables));
84 }
85 
86 /***** ***** ***** ***** ***** Rectangle ***** ***** ***** ***** *****/
87 
89  : rect_bounded_shape(cfg)
90  , border_thickness_(cfg["border_thickness"].to_int())
91  , border_color_(cfg["border_color"], color_t::null_color())
92  , fill_color_(cfg["fill_color"], color_t::null_color())
93 {
94  // Check if a raw color string evaluates to a null color.
95  if(!border_color_.has_formula() && border_color_().null()) {
97  }
98 
99  const std::string& debug = (cfg["debug"]);
100  if(!debug.empty()) {
101  DBG_GUI_P << "Rectangle: found debug message '" << debug << "'.";
102  }
103 }
104 
106 {
107  const rect area {
108  x_(variables),
109  y_(variables),
110  w_(variables),
111  h_(variables)
112  };
113 
114  const color_t fill_color = fill_color_(variables);
115 
116  // Fill the background, if applicable
117  if(!fill_color.null()) {
118  DBG_GUI_D << "fill " << fill_color;
119  draw::set_color(fill_color);
120  draw::fill(area.padded_by(-border_thickness_));
121  }
122 
123  const color_t border_color = border_color_(variables);
124 
125  // Draw the border
126  draw::set_color(border_color);
127  DBG_GUI_D << "border thickness " << border_thickness_ << ", colour " << border_color;
128  for(int i = 0; i < border_thickness_; ++i) {
129  draw::rect(area.padded_by(-i));
130  }
131 }
132 
133 /***** ***** ***** ***** ***** Rounded Rectangle ***** ***** ***** ***** *****/
134 
136  : rect_bounded_shape(cfg)
137  , r_(cfg["corner_radius"])
138  , border_thickness_(cfg["border_thickness"].to_int())
139  , border_color_(cfg["border_color"], color_t::null_color())
140  , fill_color_(cfg["fill_color"], color_t::null_color())
141 {
142  // Check if a raw color string evaluates to a null color.
143  if(!border_color_.has_formula() && border_color_().null()) {
144  border_thickness_ = 0;
145  }
146 
147  const std::string& debug = (cfg["debug"]);
148  if(!debug.empty()) {
149  DBG_GUI_P << "Rounded Rectangle: found debug message '" << debug << "'.";
150  }
151 }
152 
154 {
155  const int x = x_(variables);
156  const int y = y_(variables);
157  const int w = w_(variables);
158  const int h = h_(variables);
159  const int r = r_(variables);
160 
161  DBG_GUI_D << "Rounded Rectangle: draw from " << x << ',' << y << " width " << w << " height " << h << ".";
162 
163  const color_t fill_color = fill_color_(variables);
164 
165  // Fill the background, if applicable
166  if(!fill_color.null() && w && h) {
167  draw::set_color(fill_color);
168 
169  draw::fill(rect{x + r, y + border_thickness_, w - r * 2, r - border_thickness_ + 1});
170  draw::fill(rect{x + border_thickness_, y + r + 1, w - border_thickness_ * 2, h - r * 2});
171  draw::fill(rect{x + r, y - r + h + 1, w - r * 2, r - border_thickness_});
172 
173  draw::disc(x + r, y + r, r, 0xc0);
174  draw::disc(x + w - r, y + r, r, 0x03);
175  draw::disc(x + r, y + h - r, r, 0x30);
176  draw::disc(x + w - r, y + h - r, r, 0x0c);
177  }
178 
179  const color_t border_color = border_color_(variables);
180 
181  // Draw the border
182  draw::set_color(border_color);
183 
184  for(int i = 0; i < border_thickness_; ++i) {
185  draw::line(x + r, y + i, x + w - r, y + i);
186  draw::line(x + r, y + h - i, x + w - r, y + h - i);
187 
188  draw::line(x + i, y + r, x + i, y + h - r);
189  draw::line(x + w - i, y + r, x + w - i, y + h - r);
190 
191  draw::circle(x + r, y + r, r - i, 0xc0);
192  draw::circle(x + w - r, y + r, r - i, 0x03);
193  draw::circle(x + r, y + h - r, r - i, 0x30);
194  draw::circle(x + w - r, y + h - r, r - i, 0x0c);
195  }
196 }
197 
198 /***** ***** ***** ***** ***** CIRCLE ***** ***** ***** ***** *****/
199 
201  : shape(cfg)
202  , x_(cfg["x"])
203  , y_(cfg["y"])
204  , radius_(cfg["radius"])
205  , border_color_(cfg["border_color"])
206  , fill_color_(cfg["fill_color"])
207  , border_thickness_(cfg["border_thickness"].to_int(1))
208 {
209  const std::string& debug = (cfg["debug"]);
210  if(!debug.empty()) {
211  DBG_GUI_P << "Circle: found debug message '" << debug << "'.";
212  }
213 }
214 
216 {
217  /**
218  * @todo formulas are now recalculated every draw cycle which is a bit
219  * silly unless there has been a resize. So to optimize we should use an
220  * extra flag or do the calculation in a separate routine.
221  */
222 
223  const int x = x_(variables);
224  const int y = y_(variables);
225  const unsigned radius = radius_(variables);
226 
227  DBG_GUI_D << "Circle: drawn at " << x << ',' << y << " radius " << radius << ".";
228 
229  const color_t fill_color = fill_color_(variables);
230  if(!fill_color.null() && radius) {
231  draw::disc(x, y, radius, fill_color);
232  }
233 
234  const color_t border_color = border_color_(variables);
235  for(unsigned int i = 0; i < border_thickness_; i++) {
236  draw::circle(x, y, radius - i, border_color);
237  }
238 }
239 
240 /***** ***** ***** ***** ***** IMAGE ***** ***** ***** ***** *****/
241 
243  : shape(cfg)
244  , x_(cfg["x"])
245  , y_(cfg["y"])
246  , w_(cfg["w"])
247  , h_(cfg["h"])
248  , image_name_(cfg["name"])
249  , resize_mode_(get_resize_mode(cfg["resize_mode"]))
250  , mirror_(cfg.get_old_attribute("mirror", "vertical_mirror", "image"))
251  , actions_formula_(cfg["actions"], &functions)
252 {
253  const std::string& debug = (cfg["debug"]);
254  if(!debug.empty()) {
255  DBG_GUI_P << "Image: found debug message '" << debug << "'.";
256  }
257 }
258 
259 void image_shape::dimension_validation(unsigned value, const std::string& name, const std::string& key)
260 {
261  const int as_int = static_cast<int>(value);
262 
263  VALIDATE_WITH_DEV_MESSAGE(as_int >= 0, _("Image doesn’t fit on canvas."),
264  formatter() << "Image '" << name << "', " << key << " = " << as_int << "."
265  );
266 }
267 
269 {
270  DBG_GUI_D << "Image: draw.";
271 
272  /**
273  * @todo formulas are now recalculated every draw cycle which is a bit
274  * silly unless there has been a resize. So to optimize we should use an
275  * extra flag or do the calculation in a separate routine.
276  */
277  const std::string& name = image_name_(variables);
278 
279  if(name.empty()) {
280  DBG_GUI_D << "Image: formula returned no value, will not be drawn.";
281  return;
282  }
283 
284  // Texture filtering mode must be set on texture creation,
285  // so check whether we need smooth scaling or not here.
289  {
291  }
293 
294  if(!tex) {
295  ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn.";
296  return;
297  }
298 
299  wfl::map_formula_callable local_variables(variables);
300  local_variables.add("image_original_width", wfl::variant(tex.w()));
301  local_variables.add("image_original_height", wfl::variant(tex.h()));
302 
303  int w = w_(local_variables);
304  dimension_validation(w, name, "w");
305 
306  int h = h_(local_variables);
307  dimension_validation(h, name, "h");
308 
309  local_variables.add("image_width", wfl::variant(w ? w : tex.w()));
310  local_variables.add("image_height", wfl::variant(h ? h : tex.h()));
311 
312  const int x = x_(local_variables);
313  const int y = y_(local_variables);
314 
315  // used in gui/dialogs/story_viewer.cpp
316  local_variables.add("clip_x", wfl::variant(x));
317  local_variables.add("clip_y", wfl::variant(y));
318 
319  if (variables.has_key("fake_draw") && variables.query_value("fake_draw").as_bool()) {
320  variables.add("image_original_width", wfl::variant(tex.w()));
321  variables.add("image_original_height", wfl::variant(tex.h()));
322  variables.add("image_width", wfl::variant(w ? w : tex.w()));
323  variables.add("image_height", wfl::variant(h ? h : tex.h()));
324  return;
325  }
326 
327  // Execute the provided actions for this context.
328  wfl::variant(variables.fake_ptr()).execute_variant(actions_formula_.evaluate(local_variables));
329 
330  // If w or h is 0, assume it means the whole image.
331  if (!w) { w = tex.w(); }
332  if (!h) { h = tex.h(); }
333 
334  const SDL_Rect dst_rect { x, y, w, h };
335 
336  // What to do with the image depends on whether we need to tile it or not.
337  switch(resize_mode_) {
338  case resize_mode::tile:
339  draw::tiled(tex, dst_rect, false, mirror_(variables));
340  break;
342  draw::tiled(tex, dst_rect, true, mirror_(variables));
343  break;
345  draw::tiled_highres(tex, dst_rect, false, mirror_(variables));
346  break;
348  // Stretching is identical to scaling in terms of handling.
349  // Is this intended? That's what previous code was doing.
350  case resize_mode::scale:
351  // Filtering mode is set on texture creation above.
352  // Handling is otherwise identical to sharp scaling.
354  if(mirror_(variables)) {
355  draw::flipped(tex, dst_rect);
356  } else {
357  draw::blit(tex, dst_rect);
358  }
359  break;
360  default:
361  ERR_GUI_D << "Image: unrecognized resize mode.";
362  break;
363  }
364 }
365 
367 {
368  if(resize_mode == "tile") {
369  return resize_mode::tile;
370  } else if(resize_mode == "tile_center") {
372  } else if(resize_mode == "tile_highres") {
374  } else if(resize_mode == "stretch") {
375  return resize_mode::stretch;
376  } else if(resize_mode == "scale_sharp") {
378  } else if(resize_mode == "scale") {
379  return resize_mode::scale;
380  } else {
381  if(!resize_mode.empty()) {
382  ERR_GUI_E << "Invalid resize mode '" << resize_mode << "' falling back to 'scale'.";
383  }
384 
385  // Linear scaling just looks horrible as a default, especially on HDPI screens, and even
386  // for some non-pixel art (the logo, for example). Nearest-neighbor isn't perfect for those
387  // usecases, but it's definitely better, in my opinion.
388  //
389  // -- vultraz, 2022-08-20
391  }
392 }
393 
394 /***** ***** ***** ***** ***** TEXT ***** ***** ***** ***** *****/
395 
397  : rect_bounded_shape(cfg)
398  , cfg_(cfg)
399  , font_family_(font::str_to_family_class(cfg["font_family"]))
400  , font_size_(cfg["font_size"], font::SIZE_NORMAL)
401  , font_style_(decode_font_style(cfg["font_style"]))
402  , text_alignment_(cfg["text_alignment"])
403  , color_(cfg["color"])
404  , text_(cfg["text"])
405  , text_markup_(cfg["text_markup"], false)
406  , link_aware_(cfg["text_link_aware"], false)
407  , link_color_(cfg["text_link_color"], color_t::from_hex_string("ffff00"))
408  , maximum_width_(cfg["maximum_width"], -1)
409  , characters_per_line_(cfg["text_characters_per_line"].to_unsigned())
410  , maximum_height_(cfg["maximum_height"], -1)
411  , highlight_start_(cfg["highlight_start"])
412  , highlight_end_(cfg["highlight_end"])
413  , highlight_color_(cfg["highlight_color"], color_t::from_hex_string("215380"))
414  , attr_start_(cfg["attr_start"])
415  , attr_end_(cfg["attr_end"])
416  , attr_name_(cfg["attr_name"])
417  , attr_data_(cfg["attr_data"])
418  , outline_(cfg["outline"], false)
419  , actions_formula_(cfg["actions"], &functions)
420 {
421  const std::string& debug = (cfg["debug"]);
422  if(!debug.empty()) {
423  DBG_GUI_P << "Text: found debug message '" << debug << "'.";
424  }
425 }
426 
428 {
429  assert(variables.has_key("text"));
430 
431  // We first need to determine the size of the text which need the rendered
432  // text. So resolve and render the text first and then start to resolve
433  // the other formulas.
434  const t_string text = text_(variables);
435 
436  if(text.empty()) {
437  DBG_GUI_D << "Text: no text to render, leave.";
438  return;
439  }
440 
441  font::pango_text& text_renderer = font::get_text_renderer();
442  text_renderer.clear_attribute_list();
443 
444  //
445  // Highlight
446  //
447  std::vector<std::string> starts = utils::split(highlight_start_, ',');
448  std::vector<std::string> stops = utils::split(highlight_end_, ',');
449 
450  for(size_t i = 0; i < std::min(starts.size(), stops.size()); i++) {
451  typed_formula<int> hstart(starts.at(i));
452  typed_formula<int> hstop(stops.at(i));
453  text_renderer.add_attribute_bg_color(hstart(variables), hstop(variables), highlight_color_(variables));
454  }
455 
456  //
457  // Attribute subtags
458  //
459  for (const auto& attr : cfg_.child_range("attribute")) {
460  const std::string& name = attr["name"];
461 
462  if (name.empty()) {
463  continue;
464  }
465 
466  const unsigned start = attr["start"].to_int(0);
467  const unsigned end = attr["end"].to_int(text.size());
468 
469  if (name == "color" || name == "fgcolor" || name == "foreground") {
470  text_renderer.add_attribute_fg_color(start, end, attr["value"].empty() ? font::NORMAL_COLOR : font::string_to_color(attr["value"]));
471  } else if (name == "bgcolor"||name == "background") {
472  text_renderer.add_attribute_bg_color(start, end, attr["value"].empty() ? font::GOOD_COLOR : font::string_to_color(attr["value"]));
473  } else if (name == "font_size"||name == "size") {
474  text_renderer.add_attribute_size(start, end, attr["value"].to_int(font::SIZE_NORMAL));
475  } else if (name == "font_family"||name == "face") {
476  text_renderer.add_attribute_font_family(start, end, attr["value"].str(font::get_font_families(font::FONT_SANS_SERIF)));
477  } else if (name == "weight") {
478  text_renderer.add_attribute_weight(start, end, decode_text_weight(attr["value"]));
479  } else if (name == "style") {
480  text_renderer.add_attribute_style(start, end, decode_text_style(attr["value"]));
481  } else if (name == "bold" || name == "b") {
482  text_renderer.add_attribute_weight(start, end, PANGO_WEIGHT_BOLD);
483  } else if (name == "italic" || name == "i") {
484  text_renderer.add_attribute_style(start, end, PANGO_STYLE_ITALIC);
485  } else if (name == "underline" || name == "u") {
486  text_renderer.add_attribute_underline(start, end, PANGO_UNDERLINE_SINGLE);
487  } else {
488  // Unsupported formatting or normal text
489  text_renderer.add_attribute_weight(start, end, PANGO_WEIGHT_NORMAL);
490  text_renderer.add_attribute_style(start, end, PANGO_STYLE_NORMAL);
491  }
492  }
493 
494  text_renderer
495  .set_link_aware(link_aware_(variables))
496  .set_link_color(link_color_(variables))
497  .set_text(text, text_markup_(variables));
498 
499  text_renderer.set_family_class(font_family_)
500  .set_font_size(font_size_(variables))
502  .set_alignment(text_alignment_(variables))
503  .set_foreground_color(color_(variables))
504  .set_maximum_width(maximum_width_(variables))
505  .set_maximum_height(maximum_height_(variables), true)
506  .set_ellipse_mode(variables.has_key("text_wrap_mode")
507  ? static_cast<PangoEllipsizeMode>(variables.query_value("text_wrap_mode").as_int())
508  : PANGO_ELLIPSIZE_END)
510  .set_add_outline(outline_(variables));
511 
512  wfl::map_formula_callable local_variables(variables);
513  const auto [tw, th] = text_renderer.get_size();
514 
515  // Translate text width and height back to draw-space, rounding up.
516  local_variables.add("text_width", wfl::variant(tw));
517  local_variables.add("text_height", wfl::variant(th));
518 
519  if (variables.has_key("fake_draw") && variables.query_value("fake_draw").as_bool()) {
520  variables.add("text_width", wfl::variant(tw));
521  variables.add("text_height", wfl::variant(th));
522  return;
523  }
524 
525  const int x = x_(local_variables);
526  const int y = y_(local_variables);
527  const int w = w_(local_variables);
528  const int h = h_(local_variables);
529  rect dst_rect{x, y, w, h};
530 
531  // Execute the provided actions for this context.
532  wfl::variant(variables.fake_ptr()).execute_variant(actions_formula_.evaluate(local_variables));
533 
534  texture tex = text_renderer.render_and_get_texture();
535  if(!tex) {
536  DBG_GUI_D << "Text: Rendering '" << text << "' resulted in an empty canvas, leave.";
537  return;
538  }
539 
540  dst_rect.w = std::min(dst_rect.w, tex.w());
541  dst_rect.h = std::min(dst_rect.h, tex.h());
542 
543  draw::blit(tex, dst_rect);
544 }
545 
546 /***** ***** ***** ***** ***** CANVAS ***** ***** ***** ***** *****/
547 
549  : shapes_()
550  , blur_depth_(0)
551  , blur_region_(sdl::empty_rect)
552  , deferred_(false)
553  , w_(0)
554  , h_(0)
555  , variables_()
556  , functions_()
557 {
558 }
559 
561  : shapes_(std::move(c.shapes_))
562  , blur_depth_(c.blur_depth_)
563  , blur_region_(c.blur_region_)
564  , deferred_(c.deferred_)
565  , w_(c.w_)
566  , h_(c.h_)
567  , variables_(c.variables_)
568  , functions_(c.functions_)
569 {
570 }
571 
572 // It would be better if the blur effect was managed at a higher level.
573 // But for now this works and should be both general and robust.
574 bool canvas::update_blur(const rect& screen_region, bool force)
575 {
576  if(!blur_depth_) {
577  // No blurring needed.
578  return true;
579  }
580 
581  if(screen_region != blur_region_) {
582  DBG_GUI_D << "blur region changed from " << blur_region_
583  << " to " << screen_region;
584  // something has changed. regenerate the texture.
586  blur_region_ = screen_region;
587  }
588 
589  if(blur_texture_ && !force) {
590  // We already made the blur. It's expensive, so don't do it again.
591  return true;
592  }
593 
594  // To blur what is underneath us, it must already be rendered somewhere.
595  // This is okay for sub-elements of an opaque window (panels on the main
596  // title screen for example) as the window will already have rendered
597  // its background to the render buffer before we get here.
598  // If however we are blurring elements behind the window, such as if
599  // the window itself is translucent (objectives popup), or it is
600  // transparent with a translucent element (character dialogue),
601  // then we need to render what will be behind it before capturing that
602  // and rendering a blur.
603  // We could use the previous render frame, but there could well have been
604  // another element there last frame such as a popup window which we
605  // don't want to be part of the blur.
606  // The stable solution is to render in multiple passes,
607  // so that is what we shall do.
608 
609  // For the first pass, this element and its children are not rendered.
610  if(!deferred_) {
611  DBG_GUI_D << "Deferring blur at " << screen_region;
612  deferred_ = true;
614  return false;
615  }
616 
617  // For the second pass we read the result of the first pass at
618  // this widget's location, and blur it.
619  DBG_GUI_D << "Blurring " << screen_region << " depth " << blur_depth_;
620  rect read_region = screen_region;
621  auto setter = draw::set_render_target({});
622  surface s = video::read_pixels_low_res(&read_region);
623  blur_surface(s, {0, 0, s->w, s->h}, blur_depth_);
625  deferred_ = false;
626  return true;
627 }
628 
630 {
632 }
633 
635 {
636  // This early-return has to come before the `validate(rect.w <= w_)` check, as during the boost_unit_tests execution
637  // the debug_clock widget will have no shapes, 0x0 size, yet be given a larger rect to draw.
638  if(shapes_.empty()) {
639  DBG_GUI_D << "Canvas: empty (no shapes to draw).";
640  return;
641  }
642 
643  if(deferred_) {
644  // We will draw next frame.
645  return;
646  }
647 
648  // Draw blurred background.
649  // TODO: hwaccel - this should be able to be removed at some point with shaders
650  if(blur_depth_ && blur_texture_) {
651  DBG_GUI_D << "blitting blur size " << blur_texture_.draw_size();
653  }
654 
655  // Draw items
656  for(auto& shape : shapes_) {
657  const lg::scope_logger inner_scope_logging_object__{log_gui_draw, "Canvas: draw shape."};
659  }
660 }
661 
662 void canvas::parse_cfg(const config& cfg)
663 {
664  log_scope2(log_gui_parse, "Canvas: parsing config.");
665 
666  for(const auto [type, data] : cfg.all_children_view())
667  {
668  DBG_GUI_P << "Canvas: found shape of the type " << type << ".";
669 
670  if(type == "line") {
671  shapes_.emplace_back(std::make_unique<line_shape>(data));
672  } else if(type == "rectangle") {
673  shapes_.emplace_back(std::make_unique<rectangle_shape>(data));
674  } else if(type == "round_rectangle") {
675  shapes_.emplace_back(std::make_unique<round_rectangle_shape>(data));
676  } else if(type == "circle") {
677  shapes_.emplace_back(std::make_unique<circle_shape>(data));
678  } else if(type == "image") {
679  shapes_.emplace_back(std::make_unique<image_shape>(data, functions_));
680  } else if(type == "text") {
681  shapes_.emplace_back(std::make_unique<text_shape>(data, functions_));
682  } else if(type == "pre_commit") {
683 
684  /* note this should get split if more preprocessing is used. */
685  for(const auto [func_key, func_cfg] : data.all_children_view())
686  {
687  if(func_key == "blur") {
688  blur_depth_ = func_cfg["depth"].to_unsigned();
689  } else {
690  ERR_GUI_P << "Canvas: found a pre commit function"
691  << " of an invalid type " << type << ".";
692  }
693  }
694 
695  } else {
696  ERR_GUI_P << "Canvas: found a shape of an invalid type " << type
697  << ".";
698  }
699  }
700 }
701 
703 {
705  variables_.add("width", wfl::variant(w_));
706  variables_.add("height", wfl::variant(h_));
707 }
708 
710 {
711  w_ = size.x;
712  h_ = size.y;
714 }
715 
716 void canvas::clear_shapes(const bool force)
717 {
718  if(force) {
719  shapes_.clear();
720  } else {
721  utils::erase_if(shapes_, [](const std::unique_ptr<shape>& s) { return !s->immutable(); });
722  }
723 }
724 
725 /***** ***** ***** ***** ***** SHAPE ***** ***** ***** ***** *****/
726 
727 } // 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:172
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
child_itors child_range(config_key_type key)
Definition: config.cpp:272
Text class.
Definition: text.hpp:79
pango_text & set_font_style(const FONT_STYLE font_style)
Definition: text.cpp:544
void add_attribute_weight(const unsigned start_offset, const unsigned end_offset, PangoWeight weight)
Add pango font weight attribute to a specific portion of text.
Definition: text.cpp:351
point get_size()
Returns the size of the text, in drawing coordinates.
Definition: text.cpp:150
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:579
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:554
void add_attribute_font_family(const unsigned start_offset, const unsigned end_offset, std::string family)
Add pango font family attribute to a specific portion of text.
Definition: text.cpp:439
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:522
void add_attribute_fg_color(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Add pango fg color attribute to a specific portion of text.
Definition: text.cpp:419
void add_attribute_size(const unsigned start_offset, const unsigned end_offset, int size)
Add pango font size attribute to a specific portion of text.
Definition: text.cpp:332
pango_text & set_add_outline(bool do_add)
Definition: text.cpp:677
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:615
void add_attribute_bg_color(const unsigned start_offset, const unsigned end_offset, const color_t &color)
Mark a specific portion of text for highlighting.
Definition: text.cpp:458
void add_attribute_underline(const unsigned start_offset, const unsigned end_offset, PangoUnderline underline)
Add pango underline attribute to a specific portion of text.
Definition: text.cpp:388
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:635
pango_text & set_font_size(unsigned font_size)
Definition: text.cpp:532
pango_text & set_link_aware(bool b)
Definition: text.cpp:658
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:484
void add_attribute_style(const unsigned start_offset, const unsigned end_offset, PangoStyle style)
Add pango font style attribute to a specific portion of text, used to set italic/oblique text.
Definition: text.cpp:369
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:590
pango_text & set_maximum_width(int width)
Definition: text.cpp:563
texture render_and_get_texture()
Returns the cached texture, or creates a new one otherwise.
Definition: text.cpp:118
pango_text & set_link_color(const color_t &color)
Definition: text.cpp:667
void clear_attribute_list()
Clears all attributes from the global attribute list.
Definition: text.cpp:479
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:716
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:574
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:662
void queue_reblur()
Clear the cached blur texture, forcing it to regenerate.
Definition: canvas.cpp:629
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:702
unsigned h_
The full height of the canvas.
Definition: canvas.hpp:191
void draw()
Draw the canvas' shapes onto the screen.
Definition: canvas.cpp:634
void set_size(const point &size)
Definition: canvas.cpp:709
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:200
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:215
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:366
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:259
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:268
image_shape(const config &cfg, wfl::action_function_symbol_table &functions)
Constructor.
Definition: canvas.cpp:242
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:51
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:66
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:88
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:105
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:153
typed_formula< int > r_
The radius of the corners.
round_rectangle_shape(const config &cfg)
Constructor.
Definition: canvas.cpp:135
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_
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.
typed_formula< bool > link_aware_
The link aware switch of the text.
typed_formula< PangoAlignment > text_alignment_
The alignment of the text.
config cfg_
the source config
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:427
text_shape(const config &cfg, wfl::action_function_symbol_table &functions)
Constructor.
Definition: canvas.cpp:396
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.
bool has_formula() const
Determine whether the class contains a formula.
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
bool empty() const
Definition: tstring.hpp:194
std::string::size_type size() const
Definition: tstring.hpp:195
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:1028
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:279
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:664
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
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Graphical text output.
@ FONT_SANS_SERIF
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:1136
const color_t GOOD_COLOR
color_t string_to_color(const std::string &cmp_str)
Return the color the string represents.
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
family_class str_to_family_class(const std::string &str)
const int SIZE_NORMAL
Definition: constants.cpp:20
const color_t NORMAL_COLOR
Generic file dialog.
void get_screen_size_variables(wfl::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:125
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:53
font::pango_text::FONT_STYLE decode_font_style(const std::string &style)
Converts a font style string to a font style.
Definition: helper.cpp:31
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:74
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:920
scale_quality
Definition: picture.hpp:172
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
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition: general.hpp:100
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:622
std::string_view data
Definition: picture.cpp:178
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
void blur_surface(surface &surf, SDL_Rect rect, int depth)
Cross-fades a surface in place.
Definition: utils.cpp:961
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 h