The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
canvas.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 - 2016 by Mark de Wever <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Implementation of canvas.hpp.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
22 #include "gui/core/canvas.hpp"
23 
24 #include "config.hpp"
25 #include "image.hpp"
26 #include "formatter.hpp"
27 #include "gettext.hpp"
29 #include "gui/core/log.hpp"
30 #include "gui/widgets/helper.hpp"
31 #include "sdl/rect.hpp"
32 #include "text.hpp"
33 #include "video.hpp"
34 #include "wml_exception.hpp"
35 
36 namespace gui2
37 {
38 
39 namespace
40 {
41 
42 /*WIKI
43  * @page = GUICanvasWML
44  *
45  * {{Autogenerated}}
46  *
47  * = Canvas =
48  *
49  * A canvas is a blank drawing area on which the user can draw several shapes.
50  * The drawing is done by adding WML structures to the canvas.
51  */
52 
53 /*WIKI
54  * @page = GUICanvasWML
55  * @begin{parent}{name="generic/state/draw/"}
56  *
57  * == Pre commit ==
58  * @begin{tag}{name="pre_commit"}{min="0"}{max="1"}
59  *
60  * This section contains the pre commit functions. These functions will be
61  * executed before the drawn canvas is applied on top of the normal
62  * background. There should only be one pre commit section and its order
63  * regarding the other shapes doesn't matter. The function has effect on the
64  * entire canvas, it's not possible to affect only a small part of the canvas.
65  *
66  * The section can have one of the following subsections.
67  *
68  * === Blur ===
69  * @begin{tag}{name="blur"}{min="0"}{max="1"}
70  *
71  * Blurs the background before applying the canvas. This doesn't make sense
72  * if the widget isn't semi-transparent.
73  *
74  * Keys:
75  * @begin{table}{config}
76  * depth & unsigned & 0 & The depth to blur. $
77  * @end{table}
78  * @end{tag}{name="blur"}
79  * @end{tag}{name="pre_commit"}
80  */
81 
82 /***** ***** ***** ***** ***** DRAWING PRIMITIVES ***** ***** ***** ***** *****/
83 
84 /**
85  * Draws a single pixel on a surface.
86  *
87  * @pre The caller needs to make sure the selected coordinate
88  * fits on the @p surface.
89  * @pre The @p canvas is locked.
90  *
91  * @param start The memory address which is the start of the surface
92  * buffer to draw in.
93  * @param color The color of the pixel to draw.
94  * @param w The width of the surface.
95  * @param x The x coordinate of the pixel to draw.
96  * @param y The y coordinate of the pixel to draw.
97  */
98 static void put_pixel(const ptrdiff_t start,
99  const Uint32 color,
100  const unsigned w,
101  const unsigned x,
102  const unsigned y)
103 {
104  *reinterpret_cast<Uint32*>(start + (y* w* 4) + x* 4) = color;
105 }
106 
107 /**
108  * Draws a line on a surface.
109  *
110  * @pre The caller needs to make sure the entire line fits on
111  * the @p surface.
112  * @pre @p x2 >= @p x1
113  * @pre The @p surface is locked.
114  *
115  * @param canvas The canvas to draw upon, the caller should lock the
116  * surface before calling.
117  * @param color The color of the line to draw.
118  * @param x1 The start x coordinate of the line to draw.
119  * @param y1 The start y coordinate of the line to draw.
120  * @param x2 The end x coordinate of the line to draw.
121  * @param y2 The end y coordinate of the line to draw.
122  */
123 static void draw_line(surface& canvas,
124  Uint32 color,
125  unsigned x1,
126  unsigned y1,
127  const unsigned x2,
128  unsigned y2)
129 {
130  color = SDL_MapRGBA(canvas->format,
131  ((color & 0xFF000000) >> 24),
132  ((color & 0x00FF0000) >> 16),
133  ((color & 0x0000FF00) >> 8),
134  ((color & 0x000000FF)));
135 
136  ptrdiff_t start = reinterpret_cast<ptrdiff_t>(canvas->pixels);
137  unsigned w = canvas->w;
138 
139  DBG_GUI_D << "Shape: draw line from " << x1 << ',' << y1 << " to " << x2
140  << ',' << y2 << " canvas width " << w << " canvas height "
141  << canvas->h << ".\n";
142 
143  assert(static_cast<int>(x1) < canvas->w);
144  assert(static_cast<int>(x2) < canvas->w);
145  assert(static_cast<int>(y1) < canvas->h);
146  assert(static_cast<int>(y2) < canvas->h);
147 
148  // use a special case for vertical lines
149  if(x1 == x2) {
150  if(y2 < y1) {
151  std::swap(y1, y2);
152  }
153 
154  for(unsigned y = y1; y <= y2; ++y) {
155  put_pixel(start, color, w, x1, y);
156  }
157  return;
158  }
159 
160  // use a special case for horizontal lines
161  if(y1 == y2) {
162  for(unsigned x = x1; x <= x2; ++x) {
163  put_pixel(start, color, w, x, y1);
164  }
165  return;
166  }
167 
168  // Algorithm based on
169  // http://de.wikipedia.org/wiki/Bresenham-Algorithmus#Kompakte_Variante
170  // version of 26.12.2010.
171  const int dx = x2 - x1; // precondition x2 >= x1
172  const int dy = abs(static_cast<int>(y2 - y1));
173  const int step_x = 1;
174  const int step_y = y1 < y2 ? 1 : -1;
175  int err = (dx > dy ? dx : -dy) / 2;
176  int e2;
177 
178  for(;;) {
179  put_pixel(start, color, w, x1, y1);
180  if(x1 == x2 && y1 == y2) {
181  break;
182  }
183  e2 = err;
184  if(e2 > -dx) {
185  err -= dy;
186  x1 += step_x;
187  }
188  if(e2 < dy) {
189  err += dx;
190  y1 += step_y;
191  }
192  }
193 }
194 
195 /**
196  * Draws a circle on a surface.
197  *
198  * @pre The circle must fit on the canvas.
199  * @pre The @p surface is locked.
200  *
201  * @param canvas The canvas to draw upon, the caller should lock the
202  * surface before calling.
203  * @param color The color of the circle to draw.
204  * @param x_center The x coordinate of the center of the circle to draw.
205  * @param y_center The y coordinate of the center of the circle to draw.
206  * @param radius The radius of the circle to draw.
207  */
208 static void draw_circle(surface& canvas,
209  Uint32 color,
210  const unsigned x_center,
211  const unsigned y_center,
212  const unsigned radius)
213 {
214  color = SDL_MapRGBA(canvas->format,
215  ((color & 0xFF000000) >> 24),
216  ((color & 0x00FF0000) >> 16),
217  ((color & 0x0000FF00) >> 8),
218  ((color & 0x000000FF)));
219 
220  ptrdiff_t start = reinterpret_cast<ptrdiff_t>(canvas->pixels);
221  unsigned w = canvas->w;
222 
223  DBG_GUI_D << "Shape: draw circle at " << x_center << ',' << y_center
224  << " with radius " << radius << " canvas width " << w
225  << " canvas height " << canvas->h << ".\n";
226 
227  assert(static_cast<int>(x_center + radius) < canvas->w);
228  assert(static_cast<int>(x_center - radius) >= 0);
229  assert(static_cast<int>(y_center + radius) < canvas->h);
230  assert(static_cast<int>(y_center - radius) >= 0);
231 
232  // Algorithm based on
233  // http://de.wikipedia.org/wiki/Rasterung_von_Kreisen#Methode_von_Horn
234  // version of 2011.02.07.
235  int d = -static_cast<int>(radius);
236  int x = radius;
237  int y = 0;
238  while(!(y > x)) {
239  put_pixel(start, color, w, x_center + x, y_center + y);
240  put_pixel(start, color, w, x_center + x, y_center - y);
241  put_pixel(start, color, w, x_center - x, y_center + y);
242  put_pixel(start, color, w, x_center - x, y_center - y);
243 
244  put_pixel(start, color, w, x_center + y, y_center + x);
245  put_pixel(start, color, w, x_center + y, y_center - x);
246  put_pixel(start, color, w, x_center - y, y_center + x);
247  put_pixel(start, color, w, x_center - y, y_center - x);
248 
249  d += 2 * y + 1;
250  ++y;
251  if(d > 0) {
252  d += -2 * x + 2;
253  --x;
254  }
255  }
256 }
257 
258 /***** ***** ***** ***** ***** LINE ***** ***** ***** ***** *****/
259 
260 /** Definition of a line shape. */
261 class tline : public tcanvas::tshape
262 {
263 public:
264  /**
265  * Constructor.
266  *
267  * @param cfg The config object to define the line see
268  * http://www.wesnoth.org/wiki/GUICanvasWML#Line
269  * for more information.
270  */
271  explicit tline(const config& cfg);
272 
273  /** Implement shape::draw(). */
274  void draw(surface& canvas,
275  const game_logic::map_formula_callable& variables);
276 
277 private:
278  tformula<unsigned> x1_, /**< The start x coordinate of the line. */
279  y1_, /**< The start y coordinate of the line. */
280  x2_, /**< The end x coordinate of the line. */
281  y2_; /**< The end y coordinate of the line. */
282 
283  /** The color of the line. */
284  Uint32 color_;
285 
286  /**
287  * The thickness of the line.
288  *
289  * if the value is odd the x and y are the middle of the line.
290  * if the value is even the x and y are the middle of a line
291  * with width - 1. (0 is special case, does nothing.)
292  */
293  unsigned thickness_;
294 };
295 
296 /*WIKI
297  * @page = GUICanvasWML
298  *
299  * == Line ==
300  * @begin{tag}{name="line"}{min="0"}{max="-1"}
301  * Definition of a line. When drawing a line it doesn't get blended on the
302  * surface but replaces the pixels instead. A blitting flag might be added later
303  * if needed.
304  *
305  * Keys:
306  * @begin{table}{config}
307  * x1 & f_unsigned & 0 & The x coordinate of the startpoint. $
308  * y1 & f_unsigned & 0 & The y coordinate of the startpoint. $
309  * x2 & f_unsigned & 0 & The x coordinate of the endpoint. $
310  * y2 & f_unsigned & 0 & The y coordinate of the endpoint. $
311  * color & color & "" & The color of the line. $
312  * thickness & unsigned & 0 & The thickness of the line if 0 nothing
313  * is drawn. $
314  * debug & string & "" & Debug message to show upon creation
315  * this message is not stored. $
316  * @end{table}
317  * @end{tag}{name="line"}
318  *
319  * <span id="general_variables">Variables:</span>.
320  * @begin{table}{formula}
321  * width & unsigned & The width of the canvas. $
322  * height & unsigned & The height of the canvas. $
323  * text & tstring & The text to render on the widget. $
324  * text_maximum_width & unsigned & The maximum width available for the
325  * text on the widget. $
326  * text_maximum_height & unsigned & The maximum height available for the
327  * text on the widget. $
328  * text_wrap_mode & int & When the text doesn't fit in the
329  * available width there are several ways
330  * to fix that. This variable holds the
331  * best method. (NOTE this is a 'hidden'
332  * variable meant to copy state from a
333  * widget to its canvas so there's no
334  * reason to use this variable and thus
335  * its values are not listed and might
336  * change without further notice.) $
337  * text_alignment & h_align & The way the text is aligned inside the
338  * canvas. $
339  *@end{table}
340  *
341  * The size variables are copied to the window and will be determined at
342  * runtime. This is needed since the main window can be resized and the dialog
343  * needs to resize accordingly. The following variables are available:
344  * @begin{table}{formula}
345  * screen_width & unsigned & The usable width of the Wesnoth main
346  * window. $
347  * screen_height & unsigned & The usable height of the Wesnoth main
348  * window. $
349  * gamemapx_offset & unsigned & The distance between left edge of the
350  * screen and the game map. $
351  * gamemap_width & unsigned & The usable width of the Wesnoth gamemap,
352  * if no gamemap shown it's the same value
353  * as screen_width. $
354  * gamemap_height & unsigned & The usable height of the Wesnoth
355  * gamemap, if no gamemap shown it's the
356  * same value as screen_height. $
357  *
358  * mouse_x & unsigned & The x coordinate of the mouse pointer. $
359  * mouse_y & unsigned & The y coordinate of the mouse pointer. $
360  *
361  * window_width & unsigned & The window width. This value has two
362  * meanings during the layout phase. This
363  * only applies if automatic placement is
364  * not enabled.
365  * - When set to 0 it should return the
366  * wanted maximum width. If no maximum
367  * is wanted it should be set to the
368  * '"(screen_width)"'.
369  * - When not equal to 0 its value is the
370  * best width for the window. When the
371  * size should remain unchanged it
372  * should be set to '"(window_width)"'.
373  * $
374  *
375  * window_height & unsigned & The window height. This value has two
376  * meanings during the layout phase. This
377  * only applies if automatic placement is
378  * not enabled.
379  * - When set to 0 it should return the
380  * wanted maximum height. If no maximum
381  * is wanted it should be set to the
382  * '"(screen_height)"'.
383  * - When not equal to 0 its value is the
384  * best height for the window. When the
385  * size should remain unchanged it
386  * should be set to '"(window_height)"'.
387  * $
388  *
389  * size_request_mode & string & A field foo:
390  * - maximum
391  * - size
392  *
393  * @end{table}
394  *
395  * Note when drawing the valid coordinates are:<br>
396  * 0 -> width - 1 <br>
397  * 0 -> height -1
398  *
399  * Drawing outside this area will result in unpredictable results including
400  * crashing. (That should be fixed, when encountered.)
401  */
402 
403 /*WIKI - unclassified
404  * This code can be used by a parser to generate the wiki page
405  * structure
406  * [tag name]
407  * param type_info description
408  *
409  * param Name of the parameter.
410  *
411  * type_info = ( type = default_value) The info about a optional parameter.
412  * type_info = ( type ) The info about a mandatory parameter
413  * type_info = [ type_info ] The info about a conditional parameter
414  * description should explain the reason.
415  *
416  * description Description of the parameter.
417  *
418  *
419  *
420  *
421  * Formulas are a function between brackets, that way the engine can see whether
422  * there is standing a plain number or a formula eg:
423  * 0 A value of zero
424  * (0) A formula returning zero
425  *
426  * When formulas are available the text should state the available variables
427  * which are available in that function.
428  */
429 
430 /*WIKI
431  * @page = GUIVariable
432  *
433  * {{Autogenerated}}
434  *
435  * = Variables =
436  *
437  * In various parts of the GUI there are several variables types in use. This
438  * page describes them.
439  *
440  * == Simple types ==
441  *
442  * The simple types are types which have one value or a short list of options.
443  *
444  * @begin{table}{variable_types}
445  * unsigned & Unsigned number (positive whole numbers
446  * and zero). $
447  * f_unsigned & Unsigned number or formula returning an
448  * unsigned number. $
449  * int & Signed number (whole numbers). $
450  * f_int & Signed number or formula returning an
451  * signed number. $
452  * bool & A boolean value accepts the normal
453  * values as the rest of the game. $
454  * f_bool & Boolean value or a formula returning a
455  * boolean value. $
456  * string & A text. $
457  * tstring & A translatable string. $
458  * f_tstring & Formula returning a translatable string.
459  * $
460  * function & A string containing a set of function
461  * definition for the formula language. $
462  *
463  * color & A string which contains the color, this
464  * a group of 4 numbers between 0 and 255
465  * separated by a comma. The numbers are red
466  * component, green component, blue
467  * component and alpha. A color of 0 is not
468  * available. An alpha of 255 is fully
469  * transparent. Omitted values are set to
470  * 0. $
471  *
472  * font_style & A string which contains the style of the
473  * font:
474  * @* normal normal font
475  * @* bold bold font
476  * @* italic italic font
477  * @* underline underlined font
478  * @-Since SDL has problems combining these
479  * styles only one can be picked. Once SDL
480  * will allow multiple options, this type
481  * will be transformed to a comma separated
482  * list. If empty we default to the normal
483  * style. Since the render engine is
484  * replaced by Pango markup this field will
485  * change later on. Note widgets that allow
486  * marked up text can use markup to change
487  * the font style. $
488  *
489  * v_align & Vertical alignment; how an item is
490  * aligned vertically in the available
491  * space. Possible values:
492  * @* top aligned at the top
493  * @* bottom aligned at the bottom
494  * @* center centered
495  * @-When nothing is set or an another
496  * value as in the list the item is
497  * centered. $
498  *
499  * h_align & Horizontal alignment; how an item is
500  * aligned horizontal in the available
501  * space. Possible values:
502  * @* left aligned at the left side
503  * @* right aligned at the right side
504  * @* center centered $
505  *
506  * f_h_align & A horizontal alignment or a formula
507  * returning a horizontal alignment. $
508  *
509  * border & Comma separated list of borders to use.
510  * Possible values:
511  * @* left border at the left side
512  * @* right border at the right side
513  * @* top border at the top
514  * @* bottom border at the bottom
515  * @* all alias for "left, right, top,
516  * bottom" $
517  *
518  * scrollbar_mode & How to show the scrollbar of a widget.
519  * Possible values:
520  * @* always The scrollbar is always
521  * shown, regardless whether it's required
522  * or not.
523  * @* never The scrollbar is never
524  * shown, even not when needed. (Note when
525  * setting this mode dialogs might
526  * not properly fit anymore).
527  * @* auto Shows the scrollbar when
528  * needed. The widget will reserve space for
529  * the scrollbar, but only show when needed.
530  * @* initial_auto Like auto, but when the
531  * scrollbar is not needed the space is not
532  * reserved.
533  * @-Use auto when the list can be changed
534  * dynamically eg the game list in the
535  * lobby. For optimization you can also
536  * use auto when you really expect a
537  * scrollbar, but don't want it to be shown
538  * when not needed eg the language list
539  * will need a scrollbar on most screens. $
540  *
541  * resize_mode & Determines how an image is resized.
542  * Possible values:
543  * @* scale The image is scaled.
544  * @* stretch The first row or column
545  * of pixels is copied over the entire
546  * image. (Can only be used to scale resize
547  * in one direction, else falls
548  * back to scale.)
549  * @* tile The image is placed
550  * several times until the entire surface
551  * is filled. The last images are
552  * truncated. $
553  *
554  * grow_direction & Determines how an image is resized.
555  * Possible values:
556  * @* scale The image is scaled.
557  * @* stretch The first row or column
558  * of pixels is copied over the entire
559  * image. (Can only be used to scale resize
560  * in one direction, else falls
561  * back to scale.)
562  * @* tile The image is placed
563  * several times until the entire surface
564  * is filled. The last images are
565  * truncated. $
566  * @end{table}
567  * @allow{type}{name="unsigned"}{value="^\d+$"}
568  * @allow{type}{name="f_unsigned"}{value="^.+$"}
569  * @allow{type}{name="int"}{value="^-?\d+$"}
570  * @allow{type}{name="f_int"}{value="^.*$"}
571  * @allow{type}{name="bool"}{value="^true|false|yes|no$"}
572  * @allow{type}{name="f_bool"}{value="^.*$"}
573  * @allow{type}{name="string"}{value="^.*$"}
574  * @allow{type}{name="t_string"}{value="^_?.*$"}
575  * @allow{type}{name="f_string"}{value="^.*$"}
576  * @allow{type}{name="f_tstring"}{value="^_?.*$"}
577  * @allow{type}{name="function"}{value="^_?.*$"}
578  *
579  * @allow{type}{name="color"}{value="^(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)$"}
580  *
581  * @allow{type}{name="font_style"}{value="^(normal|bold|italic|underline)?$"}
582  * @allow{type}{name="v_align"}{value="^top|bottom|center$"}
583  * @allow{type}{name="h_align"}{value="^left|right|center$"}
584  * @allow{type}{name="f_h_align"}{value="^.*$"}
585  * @allow{type}{name="border"}{value="^(top|bottom|left|right|all)?(,\s*(top|bottom|left|right|all))*$"}
586  * @allow{type}{name="scrollbar_mode"}{value="^always|never|auto|initial_auto$"}
587  * @allow{type}{name="resize_mode"}{value="^scale|stretch|tile$"}
588  * @allow{type}{name="grow_direction"}{value="^horizontal|vertical$"}
589  *
590  * @remove{type}{name="section"}
591  * @remove{type}{name="config"}
592  * @remove{type}{name="grid"}
593  * == Section types ==
594  *
595  * For more complex parts, there are sections. Sections contain of several
596  * lines of WML and can have sub sections. For example a grid has sub sections
597  * which contain various widgets. Here's the list of sections.
598  *
599  * @begin{table}{variable_types}
600  * section & A generic section. The documentation
601  * about the section should describe the
602  * section in further detail. $
603  *
604  * grid & A grid contains several widgets. (TODO
605  * add link to generic grid page.) $
606  * @end{table}
607  */
608 
609 tline::tline(const config& cfg)
610  : x1_(cfg["x1"])
611  , y1_(cfg["y1"])
612  , x2_(cfg["x2"])
613  , y2_(cfg["y2"])
614  , color_(decode_color(cfg["color"]))
615  , thickness_(cfg["thickness"])
616 {
617  const std::string& debug = (cfg["debug"]);
618  if(!debug.empty()) {
619  DBG_GUI_P << "Line: found debug message '" << debug << "'.\n";
620  }
621 }
622 
623 void tline::draw(surface& canvas,
624  const game_logic::map_formula_callable& variables)
625 {
626  /**
627  * @todo formulas are now recalculated every draw cycle which is a bit silly
628  * unless there has been a resize. So to optimize we should use an extra
629  * flag or do the calculation in a separate routine.
630  */
631 
632  const unsigned x1 = x1_(variables);
633  const unsigned y1 = y1_(variables);
634  const unsigned x2 = x2_(variables);
635  const unsigned y2 = y2_(variables);
636 
637  DBG_GUI_D << "Line: draw from " << x1 << ',' << y1 << " to " << x2 << ','
638  << y2 << " canvas size " << canvas->w << ',' << canvas->h
639  << ".\n";
640 
641  VALIDATE(static_cast<int>(x1) < canvas->w
642  && static_cast<int>(x2) < canvas->w
643  && static_cast<int>(y1) < canvas->h
644  && static_cast<int>(y2) < canvas->h,
645  _("Line doesn't fit on canvas."));
646 
647  // @todo FIXME respect the thickness.
648 
649  // now draw the line we use Bresenham's algorithm, which doesn't
650  // support antialiasing. The advantage is that it's easy for testing.
651 
652  // lock the surface
653  surface_lock locker(canvas);
654  if(x1 > x2) {
655  // invert points
656  draw_line(canvas, color_, x2, y2, x1, y1);
657  } else {
658  draw_line(canvas, color_, x1, y1, x2, y2);
659  }
660 }
661 
662 /***** ***** ***** ***** ***** Rectangle ***** ***** ***** ***** *****/
663 
664 /** Definition of a rectangle shape. */
665 class trectangle : public tcanvas::tshape
666 {
667 public:
668  /**
669  * Constructor.
670  *
671  * @param cfg The config object to define the rectangle see
672  * http://www.wesnoth.org/wiki/GUICanvasWML#Rectangle
673  * for more information.
674  */
675  explicit trectangle(const config& cfg);
676 
677  /** Implement shape::draw(). */
678  void draw(surface& canvas,
679  const game_logic::map_formula_callable& variables);
680 
681 private:
682  tformula<unsigned> x_, /**< The x coordinate of the rectangle. */
683  y_, /**< The y coordinate of the rectangle. */
684  w_, /**< The width of the rectangle. */
685  h_; /**< The height of the rectangle. */
686 
687  /**
688  * Border thickness.
689  *
690  * If 0 the fill color is used for the entire widget.
691  */
693 
694  /**
695  * The border color of the rectangle.
696  *
697  * If the color is fully transparent the border isn't drawn.
698  */
700 
701  /**
702  * The border color of the rectangle.
703  *
704  * If the color is fully transparent the rectangle won't be filled.
705  */
706  Uint32 fill_color_;
707 };
708 
709 /*WIKI
710  * @page = GUICanvasWML
711  *
712  * == Rectangle ==
713  * @begin{tag}{name="rectangle"}{min="0"}{max="-1"}
714  *
715  * Definition of a rectangle. When drawing a rectangle it doesn't get blended on
716  * the surface but replaces the pixels instead. A blitting flag might be added
717  * later if needed.
718  *
719  * Keys:
720  * @begin{table}{config}
721  * x & f_unsigned & 0 & The x coordinate of the top left corner.
722  * $
723  * y & f_unsigned & 0 & The y coordinate of the top left corner.
724  * $
725  * w & f_unsigned & 0 & The width of the rectangle. $
726  * h & f_unsigned & 0 & The height of the rectangle. $
727  * border_thickness & unsigned & 0 &
728  * The thickness of the border if the
729  * thickness is zero it's not drawn. $
730  * border_color & color & "" & The color of the border if empty it's
731  * not drawn. $
732  * fill_color & color & "" & The color of the interior if omitted
733  * it's not drawn. $
734  * debug & string & "" & Debug message to show upon creation
735  * this message is not stored. $
736  * @end{table}
737  * @end{tag}{name="rectangle"}
738  * Variables:
739  * See [[#general_variables|Line]].
740  *
741  */
742 trectangle::trectangle(const config& cfg)
743  : x_(cfg["x"])
744  , y_(cfg["y"])
745  , w_(cfg["w"])
746  , h_(cfg["h"])
747  , border_thickness_(cfg["border_thickness"])
748  , border_color_(decode_color(cfg["border_color"]))
749  , fill_color_(decode_color(cfg["fill_color"]))
750 {
751  if(border_color_ == 0) {
752  border_thickness_ = 0;
753  }
754 
755  const std::string& debug = (cfg["debug"]);
756  if(!debug.empty()) {
757  DBG_GUI_P << "Rectangle: found debug message '" << debug << "'.\n";
758  }
759 }
760 
761 void trectangle::draw(surface& canvas,
762  const game_logic::map_formula_callable& variables)
763 {
764  /**
765  * @todo formulas are now recalculated every draw cycle which is a bit
766  * silly unless there has been a resize. So to optimize we should use an
767  * extra flag or do the calculation in a separate routine.
768  */
769  const unsigned x = x_(variables);
770  const unsigned y = y_(variables);
771  const unsigned w = w_(variables);
772  const unsigned h = h_(variables);
773 
774  DBG_GUI_D << "Rectangle: draw from " << x << ',' << y << " width " << w
775  << " height " << h << " canvas size " << canvas->w << ','
776  << canvas->h << ".\n";
777 
778  VALIDATE(static_cast<int>(x) < canvas->w
779  && static_cast<int>(x + w) <= canvas->w
780  && static_cast<int>(y) < canvas->h
781  && static_cast<int>(y + h) <= canvas->h,
782  _("Rectangle doesn't fit on canvas."));
783 
784 
785  surface_lock locker(canvas);
786 
787  // draw the border
788  for(unsigned i = 0; i < border_thickness_; ++i) {
789 
790  const unsigned left = x + i;
791  const unsigned right = left + w - (i * 2) - 1;
792  const unsigned top = y + i;
793  const unsigned bottom = top + h - (i * 2) - 1;
794 
795  // top horizontal (left -> right)
796  draw_line(canvas, border_color_, left, top, right, top);
797 
798  // right vertical (top -> bottom)
799  draw_line(canvas, border_color_, right, top, right, bottom);
800 
801  // bottom horizontal (left -> right)
802  draw_line(canvas, border_color_, left, bottom, right, bottom);
803 
804  // left vertical (top -> bottom)
805  draw_line(canvas, border_color_, left, top, left, bottom);
806  }
807 
808  if(fill_color_) {
809 
811  (x + border_thickness_),
812  (y + border_thickness_),
813  (w - (border_thickness_ * 2)),
814  (h - (border_thickness_ * 2)),
815  (fill_color_ & 0xFF000000) >> 24,
816  (fill_color_ & 0x00FF0000) >> 16,
817  (fill_color_ & 0x0000FF00) >> 8,
818  (fill_color_ & 0x000000FF) / 255.0, canvas);
819  }
820 }
821 
822 /***** ***** ***** ***** ***** CIRCLE ***** ***** ***** ***** *****/
823 
824 /** Definition of a circle shape. */
825 class tcircle : public tcanvas::tshape
826 {
827 public:
828  /**
829  * Constructor.
830  *
831  * @param cfg The config object to define the circle see
832  * http://www.wesnoth.org/wiki/GUICanvasWML#Circle
833  * for more information.
834  */
835  explicit tcircle(const config& cfg);
836 
837  /** Implement shape::draw(). */
838  void draw(surface& canvas,
839  const game_logic::map_formula_callable& variables);
840 
841 private:
842  tformula<unsigned> x_, /**< The center x coordinate of the circle. */
843  y_, /**< The center y coordinate of the circle. */
844  radius_; /**< The radius of the circle. */
845 
846  /** The color of the circle. */
847  Uint32 color_;
848 };
849 
850 /*WIKI
851  * @page = GUICanvasWML
852  *
853  * == Circle ==
854  * @begin{tag}{name="circle"}{min="0"}{max="-1"}
855  *
856  * Definition of a circle. When drawing a circle it doesn't get blended on
857  * the surface but replaces the pixels instead. A blitting flag might be
858  * added later if needed.
859  *
860  * Keys:
861  * @begin{table}{config}
862  * x & f_unsigned & 0 & The x coordinate of the center. $
863  * y & f_unsigned & 0 & The y coordinate of the center. $
864  * radius & f_unsigned & 0 & The radius of the circle if 0 nothing is
865  * drawn. $
866  * color & color & "" & The color of the circle. $
867  * debug & string & "" & Debug message to show upon creation this
868  * message is not stored. $
869  * @end{table}
870  * @end{tag}{name="circle"}
871  * Variables:
872  * See [[#general_variables|Line]].
873  *
874  * Drawing outside the area will result in unpredictable results including
875  * crashing. (That should be fixed, when encountered.)
876  */
877 tcircle::tcircle(const config& cfg)
878  : x_(cfg["x"])
879  , y_(cfg["y"])
880  , radius_(cfg["radius"])
881  , color_(decode_color(cfg["color"]))
882 {
883  const std::string& debug = (cfg["debug"]);
884  if(!debug.empty()) {
885  DBG_GUI_P << "Circle: found debug message '" << debug << "'.\n";
886  }
887 }
888 
889 void tcircle::draw(surface& canvas,
890  const game_logic::map_formula_callable& variables)
891 {
892  /**
893  * @todo formulas are now recalculated every draw cycle which is a bit
894  * silly unless there has been a resize. So to optimize we should use an
895  * extra flag or do the calculation in a separate routine.
896  */
897 
898  const unsigned x = x_(variables);
899  const unsigned y = y_(variables);
900  const unsigned radius = radius_(variables);
901 
902  DBG_GUI_D << "Circle: drawn at " << x << ',' << y << " radius " << radius
903  << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
904 
906  static_cast<int>(x - radius) >= 0,
907  _("Circle doesn't fit on canvas."),
908  (formatter() << "x = " << x << ", radius = " << radius).str());
909 
911  static_cast<int>(y - radius) >= 0,
912  _("Circle doesn't fit on canvas."),
913  (formatter() << "y = " << y << ", radius = " << radius).str());
914 
916  static_cast<int>(x + radius) < canvas->w,
917  _("Circle doesn't fit on canvas."),
918  (formatter() << "x = " << x << ", radius = " << radius
919  << "', canvas width = " << canvas->w << ".").str());
920 
922  static_cast<int>(y + radius) < canvas->h,
923  _("Circle doesn't fit on canvas."),
924  (formatter() << "y = " << y << ", radius = " << radius
925  << "', canvas height = " << canvas->h << ".").str());
926 
927  // lock the surface
928  surface_lock locker(canvas);
929  draw_circle(canvas, color_, x, y, radius);
930 }
931 
932 /***** ***** ***** ***** ***** IMAGE ***** ***** ***** ***** *****/
933 
934 /** Definition of an image shape. */
935 class timage : public tcanvas::tshape
936 {
937 public:
938  /**
939  * Constructor.
940  *
941  * @param cfg The config object to define the image see
942  * http://www.wesnoth.org/wiki/GUICanvasWML#Image
943  * for more information.
944  */
945  explicit timage(const config& cfg);
946 
947  /** Implement shape::draw(). */
948  void draw(surface& canvas,
949  const game_logic::map_formula_callable& variables);
950 
951 private:
952  tformula<unsigned> x_, /**< The x coordinate of the image. */
953  y_, /**< The y coordinate of the image. */
954  w_, /**< The width of the image. */
955  h_; /**< The height of the image. */
956 
957  /** Contains the size of the image. */
958  SDL_Rect src_clip_;
959 
960  /** The image is cached in this surface. */
962 
963 
964 
965  /**
966  * Name of the image.
967  *
968  * This value is only used when the image name is a formula. If it isn't a
969  * formula the image will be loaded in the constructor. If it's a formula it
970  * will be loaded every draw cycles. This allows 'changing' images.
971  */
972  tformula<std::string> image_name_;
973 
974  /**
975  * Determines the way an image will be resized.
976  *
977  * If the image is smaller is needed it needs to resized, how is determined
978  * by the value of this enum.
979  */
980  enum tresize_mode {
981  scale,
982  stretch,
983  tile
984  };
985 
986  /** Converts a string to a resize mode. */
987  tresize_mode get_resize_mode(const std::string& resize_mode);
988 
989  /** The resize mode for an image. */
990  tresize_mode resize_mode_;
991 
992  /** Mirror the image over the vertical axis. */
993  tformula<bool> vertical_mirror_;
994 };
995 
996 /*WIKI
997  * @page = GUICanvasWML
998  *
999  * == Image ==
1000  * @begin{tag}{name="image"}{min="0"}{max="-1"}
1001  * Definition of an image.
1002  *
1003  * Keys:
1004  * @begin{table}{config}
1005  * x & f_unsigned & 0 & The x coordinate of the top left corner.
1006  * $
1007  * y & f_unsigned & 0 & The y coordinate of the top left corner.
1008  * $
1009  * w & f_unsigned & 0 & The width of the image, if not zero the
1010  * image will be scaled to the desired
1011  * width. $
1012  * h & f_unsigned & 0 & The height of the image, if not zero the
1013  * image will be scaled to the desired
1014  * height. $
1015  * resize_mode & resize_mode & scale &
1016  * Determines how an image is scaled to fit
1017  * the wanted size. $
1018  * vertical_mirror & f_bool & false &
1019  * Mirror the image over the vertical axis.
1020  * $
1021  * name & f_string & "" & The name of the image. $
1022  * debug & string & "" & Debug message to show upon creation
1023  * this message is not stored. $
1024  *
1025  * @end{table}
1026  * @end{tag}{name="image"}
1027  * Variables:
1028  * @begin{table}{formula}
1029  * image_width & unsigned & The width of the image, either the
1030  * requested width or the natural width of
1031  * the image. This value can be used to set
1032  * the x (or y) value of the image. (This
1033  * means x and y are evaluated after the
1034  * width and height.) $
1035  * image_height & unsigned & The height of the image, either the
1036  * requested height or the natural height
1037  * of the image. This value can be used to
1038  * set the y (or x) value of the image.
1039  * (This means x and y are evaluated after
1040  * the width and height.) $
1041  * image_original_width & unsigned &
1042  * The width of the image as stored on
1043  * disk, can be used to set x or w
1044  * (also y and h can be set). $
1045  * image_original_height & unsigned &
1046  * The height of the image as stored on
1047  * disk, can be used to set y or h
1048  * (also x and y can be set). $
1049  * @end{table}
1050  * Also the general variables are available, see [[#general_variables|Line]].
1051  */
1052 timage::timage(const config& cfg)
1053  : x_(cfg["x"])
1054  , y_(cfg["y"])
1055  , w_(cfg["w"])
1056  , h_(cfg["h"])
1057  , src_clip_()
1058  , image_()
1059  , image_name_(cfg["name"])
1060  , resize_mode_(get_resize_mode(cfg["resize_mode"]))
1061  , vertical_mirror_(cfg["vertical_mirror"])
1062 {
1063  const std::string& debug = (cfg["debug"]);
1064  if(!debug.empty()) {
1065  DBG_GUI_P << "Image: found debug message '" << debug << "'.\n";
1066  }
1067 }
1068 
1069 void timage::draw(surface& canvas,
1070  const game_logic::map_formula_callable& variables)
1071 {
1072  DBG_GUI_D << "Image: draw.\n";
1073 
1074  /**
1075  * @todo formulas are now recalculated every draw cycle which is a bit
1076  * silly unless there has been a resize. So to optimize we should use an
1077  * extra flag or do the calculation in a separate routine.
1078  */
1079  const std::string& name = image_name_(variables);
1080 
1081  if(name.empty()) {
1082  DBG_GUI_D << "Image: formula returned no value, will not be drawn.\n";
1083  return;
1084  }
1085 
1086  /*
1087  * The locator might return a different surface for every call so we can't
1088  * cache the output, also not if no formula is used.
1089  */
1091 
1092  if(!tmp) {
1093  ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn."
1094  << std::endl;
1095  return;
1096  }
1097 
1099  assert(image_);
1100  src_clip_ = sdl::create_rect(0, 0, image_->w, image_->h);
1101 
1102  game_logic::map_formula_callable local_variables(variables);
1103  local_variables.add("image_original_width", variant(image_->w));
1104  local_variables.add("image_original_height", variant(image_->h));
1105 
1106  unsigned w = w_(local_variables);
1107  VALIDATE_WITH_DEV_MESSAGE(static_cast<int>(w) >= 0,
1108  _("Image doesn't fit on canvas."),
1109  (formatter() << "Image '" << name
1110  << "', w = " << static_cast<int>(w)
1111  << ".").str());
1112 
1113  unsigned h = h_(local_variables);
1114  VALIDATE_WITH_DEV_MESSAGE(static_cast<int>(h) >= 0,
1115  _("Image doesn't fit on canvas."),
1116  (formatter() << "Image '" << name
1117  << "', h = " << static_cast<int>(h)
1118  << ".").str());
1119 
1120  local_variables.add("image_width", variant(w ? w : image_->w));
1121  local_variables.add("image_height", variant(h ? h : image_->h));
1122 
1123  const unsigned x = x_(local_variables);
1124  VALIDATE_WITH_DEV_MESSAGE(static_cast<int>(x) >= 0,
1125  _("Image doesn't fit on canvas."),
1126  (formatter() << "Image '" << name
1127  << "', x = " << static_cast<int>(x)
1128  << ".").str());
1129 
1130  const unsigned y = y_(local_variables);
1131  VALIDATE_WITH_DEV_MESSAGE(static_cast<int>(y) >= 0,
1132  _("Image doesn't fit on canvas."),
1133  (formatter() << "Image '" << name
1134  << "', y = " << static_cast<int>(y)
1135  << ".").str());
1136 
1137  // Copy the data to local variables to avoid overwriting the originals.
1138  SDL_Rect src_clip = src_clip_;
1139  SDL_Rect dst_clip = sdl::create_rect(x, y, 0, 0);
1140  surface surf;
1141 
1142  // Test whether we need to scale and do the scaling if needed.
1143  if(w || h) {
1144  bool done = false;
1145  bool stretch_image = (resize_mode_ == stretch) && (!!w ^ !!h);
1146  if(!w) {
1147  if(stretch_image) {
1148  DBG_GUI_D << "Image: vertical stretch from " << image_->w << ','
1149  << image_->h << " to a height of " << h << ".\n";
1150 
1151  surf = stretch_surface_vertical(image_, h, false);
1152  done = true;
1153  }
1154  w = image_->w;
1155  }
1156 
1157  if(!h) {
1158  if(stretch_image) {
1159  DBG_GUI_D << "Image: horizontal stretch from " << image_->w
1160  << ',' << image_->h << " to a width of " << w
1161  << ".\n";
1162 
1163  surf = stretch_surface_horizontal(image_, w, false);
1164  done = true;
1165  }
1166  h = image_->h;
1167  }
1168 
1169  if(!done) {
1170 
1171  if(resize_mode_ == tile) {
1172  DBG_GUI_D << "Image: tiling from " << image_->w << ','
1173  << image_->h << " to " << w << ',' << h << ".\n";
1174 
1175  const int columns = (w + image_->w - 1) / image_->w;
1176  const int rows = (h + image_->h - 1) / image_->h;
1177  surf = create_neutral_surface(w, h);
1178 
1179  for(int x = 0; x < columns; ++x) {
1180  for(int y = 0; y < rows; ++y) {
1181  const SDL_Rect dest = sdl::create_rect(
1182  x * image_->w, y * image_->h, 0, 0);
1183  blit_surface(image_, nullptr, surf, &dest);
1184  }
1185  }
1186 
1187  } else {
1188  if(resize_mode_ == stretch) {
1189  ERR_GUI_D << "Image: failed to stretch image, "
1190  "fall back to scaling.\n";
1191  }
1192 
1193  DBG_GUI_D << "Image: scaling from " << image_->w << ','
1194  << image_->h << " to " << w << ',' << h << ".\n";
1195 
1196  surf = scale_surface(image_, w, h, false);
1197  }
1198  }
1199  src_clip.w = w;
1200  src_clip.h = h;
1201  } else {
1202  surf = image_;
1203  }
1204 
1205  if(vertical_mirror_(local_variables)) {
1206  surf = flip_surface(surf, false);
1207  }
1208 
1209  blit_surface(surf, &src_clip, canvas, &dst_clip);
1210 }
1211 
1212 timage::tresize_mode timage::get_resize_mode(const std::string& resize_mode)
1213 {
1214  if(resize_mode == "tile") {
1215  return timage::tile;
1216  } else if(resize_mode == "stretch") {
1217  return timage::stretch;
1218  } else {
1219  if(!resize_mode.empty() && resize_mode != "scale") {
1220  ERR_GUI_E << "Invalid resize mode '" << resize_mode
1221  << "' falling back to 'scale'.\n";
1222  }
1223  return timage::scale;
1224  }
1225 }
1226 
1227 /***** ***** ***** ***** ***** TEXT ***** ***** ***** ***** *****/
1228 
1229 /** Definition of a text shape. */
1230 class ttext : public tcanvas::tshape
1231 {
1232 public:
1233  /**
1234  * Constructor.
1235  *
1236  * @param cfg The config object to define the text see
1237  * http://www.wesnoth.org/wiki/GUICanvasWML#Text
1238  * for more information.
1239  */
1240  explicit ttext(const config& cfg);
1241 
1242  /** Implement shape::draw(). */
1243  void draw(surface& canvas,
1244  const game_logic::map_formula_callable& variables);
1245 
1246 private:
1247  tformula<unsigned> x_, /**< The x coordinate of the text. */
1248  y_, /**< The y coordinate of the text. */
1249  w_, /**< The width of the text. */
1250  h_; /**< The height of the text. */
1251 
1252  /** The text font family. */
1254 
1255  /** The font size of the text. */
1256  unsigned font_size_;
1257 
1258  /** The style of the text. */
1259  unsigned font_style_;
1260 
1261  /** The alignment of the text. */
1262  tformula<PangoAlignment> text_alignment_;
1263 
1264  /** The color of the text. */
1265  Uint32 color_;
1266 
1267  /** The text to draw. */
1268  tformula<t_string> text_;
1269 
1270  /** The text markup switch of the text. */
1271  tformula<bool> text_markup_;
1272 
1273  /** The link aware switch of the text. */
1274  tformula<bool> link_aware_;
1275 
1276  /** The link color of the text. */
1277  tformula<std::string> link_color_;
1278 
1279  /** The maximum width for the text. */
1280  tformula<int> maximum_width_;
1281 
1282  /** The number of characters per line. */
1284 
1285  /** The maximum height for the text. */
1286  tformula<int> maximum_height_;
1287 };
1288 
1289 /*WIKI
1290  * @page = GUICanvasWML
1291  *
1292  * == Text ==
1293  * @begin{tag}{name="text"}{min="0"}{max="-1"}
1294  * Definition of text.
1295  *
1296  * Keys:
1297  * @begin{table}{config}
1298  * x & f_unsigned & 0 & The x coordinate of the top left corner.
1299  * $
1300  * y & f_unsigned & 0 & The y coordinate of the top left corner.
1301  * $
1302  * w & f_unsigned & 0 & The width of the text's bounding
1303  * rectangle. $
1304  * h & f_unsigned & 0 & The height of the text's bounding
1305  * rectangle. $
1306  * font_family & font_family & "sans" &
1307  * The font family used for the text. $
1308  * font_size & unsigned & & The size of the text font. $
1309  * font_style & font_style & "" & The style of the text. $
1310  * text_alignment & f_h_align & "left" &
1311  * The alignment of the text. $
1312  * color & color & "" & The color of the text. $
1313  * text & f_tstring & "" & The text to draw (translatable). $
1314  * text_markup & f_bool & false & Can the text have mark-up? $
1315  * text_link_aware & f_bool & false &
1316  * Is the text link aware? $
1317  * text_link_color & f_string & "#ffff00" &
1318  * The color of links in the text $
1319  * maximum_width & f_int & -1 & The maximum width the text is allowed to
1320  * be. $
1321  * maximum_height & f_int & -1 & The maximum height the text is allowed
1322  * to be. $
1323  * debug & string & "" & Debug message to show upon creation
1324  * this message is not stored. $
1325  * @end{table}
1326  * @end{tag}{name="text"}
1327  * NOTE alignment could only be done with the formulas, but now with the
1328  * text_alignment flag as well, older widgets might still use the formulas and
1329  * not all widgets may expose the text alignment yet and when exposed not use
1330  * it yet.
1331  *
1332  * Variables:
1333  * @begin{table}{formula}
1334  * text_width & unsigned & The width of the rendered text. $
1335  * text_height & unsigned & The height of the rendered text. $
1336  * @end{table}
1337  * Also the general variables are available, see [[#general_variables|Line]].
1338  * @end{parent}{name="generic/state/draw/"}
1339  */
1340 
1341 ttext::ttext(const config& cfg)
1342  : x_(cfg["x"])
1343  , y_(cfg["y"])
1344  , w_(cfg["w"])
1345  , h_(cfg["h"])
1346  , font_family_(font::str_to_family_class(cfg["font_family"]))
1347  , font_size_(cfg["font_size"])
1348  , font_style_(decode_font_style(cfg["font_style"]))
1349  , text_alignment_(cfg["text_alignment"])
1350  , color_(decode_color(cfg["color"]))
1351  , text_(cfg["text"])
1352  , text_markup_(cfg["text_markup"], false)
1353  , link_aware_(cfg["text_link_aware"], false)
1354  , link_color_(cfg["text_link_color"], "#ffff00")
1355  , maximum_width_(cfg["maximum_width"], -1)
1356  , characters_per_line_(cfg["text_characters_per_line"])
1357  , maximum_height_(cfg["maximum_height"], -1)
1358 {
1359  VALIDATE(font_size_, _("Text has a font size of 0."));
1360 
1361  const std::string& debug = (cfg["debug"]);
1362  if(!debug.empty()) {
1363  DBG_GUI_P << "Text: found debug message '" << debug << "'.\n";
1364  }
1365 }
1366 
1367 void ttext::draw(surface& canvas,
1368  const game_logic::map_formula_callable& variables)
1369 {
1370  assert(variables.has_key("text"));
1371 
1372  // We first need to determine the size of the text which need the rendered
1373  // text. So resolve and render the text first and then start to resolve
1374  // the other formulas.
1375  const t_string text = text_(variables);
1376 
1377  if(text.empty()) {
1378  DBG_GUI_D << "Text: no text to render, leave.\n";
1379  return;
1380  }
1381 
1382  static font::ttext text_renderer;
1383 
1384  text_renderer.set_link_aware(link_aware_(variables))
1385  .set_link_color(link_color_(variables));
1386  text_renderer.set_text(text, text_markup_(variables));
1387 
1388  text_renderer
1392  .set_alignment(text_alignment_(variables))
1394  .set_maximum_width(maximum_width_(variables))
1395  .set_maximum_height(maximum_height_(variables), true)
1397  variables.has_key("text_wrap_mode")
1398  ? static_cast<PangoEllipsizeMode>(
1399  variables.query_value("text_wrap_mode")
1400  .as_int())
1401  : PANGO_ELLIPSIZE_END)
1403 
1404  surface surf = text_renderer.render();
1405  if(surf->w == 0) {
1406  DBG_GUI_D << "Text: Rendering '" << text
1407  << "' resulted in an empty canvas, leave.\n";
1408  return;
1409  }
1410 
1411  game_logic::map_formula_callable local_variables(variables);
1412  local_variables.add("text_width", variant(surf->w));
1413  local_variables.add("text_height", variant(surf->h));
1414  /*
1415  std::cerr << "Text: drawing text '" << text
1416  << " maximum width " << maximum_width_(variables)
1417  << " maximum height " << maximum_height_(variables)
1418  << " text width " << surf->w
1419  << " text height " << surf->h;
1420  */
1421  ///@todo formulas are now recalculated every draw cycle which is a
1422  // bit silly unless there has been a resize. So to optimize we should
1423  // use an extra flag or do the calculation in a separate routine.
1424 
1425  const unsigned x = x_(local_variables);
1426  const unsigned y = y_(local_variables);
1427  const unsigned w = w_(local_variables);
1428  const unsigned h = h_(local_variables);
1429 
1430  DBG_GUI_D << "Text: drawing text '" << text << "' drawn from " << x << ','
1431  << y << " width " << w << " height " << h << " canvas size "
1432  << canvas->w << ',' << canvas->h << ".\n";
1433 
1434  VALIDATE(static_cast<int>(x) < canvas->w && static_cast<int>(y) < canvas->h,
1435  _("Text doesn't start on canvas."));
1436 
1437  // A text might be to long and will be clipped.
1438  if(surf->w > static_cast<int>(w)) {
1439  WRN_GUI_D << "Text: text is too wide for the "
1440  "canvas and will be clipped.\n";
1441  }
1442 
1443  if(surf->h > static_cast<int>(h)) {
1444  WRN_GUI_D << "Text: text is too high for the "
1445  "canvas and will be clipped.\n";
1446  }
1447 
1448  SDL_Rect dst = sdl::create_rect(x, y, canvas->w, canvas->h);
1449  blit_surface(surf, 0, canvas, &dst);
1450 }
1451 
1452 } // namespace
1453 
1454 /***** ***** ***** ***** ***** CANVAS ***** ***** ***** ***** *****/
1455 
1456 tcanvas::tcanvas()
1457  : shapes_()
1458  , blur_depth_(0)
1459  , w_(0)
1460  , h_(0)
1461  , canvas_()
1462  , variables_()
1463  , is_dirty_(true)
1464 {
1465 }
1466 
1467 void tcanvas::draw(const bool force)
1468 {
1469  log_scope2(log_gui_draw, "Canvas: drawing.");
1470  if(!is_dirty_ && !force) {
1471  DBG_GUI_D << "Canvas: nothing to draw.\n";
1472  return;
1473  }
1474 
1475  if(is_dirty_) {
1477  variables_.add("width", variant(w_));
1478  variables_.add("height", variant(h_));
1479  }
1480 
1481  // create surface
1482  DBG_GUI_D << "Canvas: create new empty canvas.\n";
1484 
1485  // draw items
1487  itor != shapes_.end();
1488  ++itor) {
1489  log_scope2(log_gui_draw, "Canvas: draw shape.");
1490 
1491  (*itor)->draw(canvas_, variables_);
1492  }
1493 
1494  is_dirty_ = false;
1495 }
1496 
1497 void tcanvas::blit(surface& surf, SDL_Rect rect)
1498 {
1499  draw();
1500 
1501  if(blur_depth_) {
1502  /*
1503  * If the surf is the video surface the blurring seems to stack, this
1504  * can be seen in the title screen. So also use the not 32 bpp method
1505  * for this situation.
1506  */
1507  if(surf != get_video_surface() && is_neutral(surf)) {
1508  blur_surface(surf, rect, blur_depth_);
1509  } else {
1510  // Can't directly blur the surface if not 32 bpp.
1511  SDL_Rect r = rect;
1512  surface s = get_surface_portion(surf, r);
1513  s = blur_surface(s, blur_depth_, false);
1514  sdl_blit(s, nullptr, surf, &rect);
1515  }
1516  }
1517 
1518  sdl_blit(canvas_, nullptr, surf, &rect);
1519 }
1520 
1521 void tcanvas::parse_cfg(const config& cfg)
1522 {
1523  log_scope2(log_gui_parse, "Canvas: parsing config.");
1524  shapes_.clear();
1525 
1526  for(const auto & shape : cfg.all_children_range())
1527  {
1528  const std::string& type = shape.key;
1529  const config& data = shape.cfg;
1530 
1531  DBG_GUI_P << "Canvas: found shape of the type " << type << ".\n";
1532 
1533  if(type == "line") {
1534  shapes_.push_back(new tline(data));
1535  } else if(type == "rectangle") {
1536  shapes_.push_back(new trectangle(data));
1537  } else if(type == "circle") {
1538  shapes_.push_back(new tcircle(data));
1539  } else if(type == "image") {
1540  shapes_.push_back(new timage(data));
1541  } else if(type == "text") {
1542  shapes_.push_back(new ttext(data));
1543  } else if(type == "pre_commit") {
1544 
1545  /* note this should get split if more preprocessing is used. */
1546  for(const auto & function : data.all_children_range())
1547  {
1548 
1549  if(function.key == "blur") {
1550  blur_depth_ = function.cfg["depth"];
1551  } else {
1552  ERR_GUI_P << "Canvas: found a pre commit function"
1553  << " of an invalid type " << type << ".\n";
1554  }
1555  }
1556 
1557  } else {
1558  ERR_GUI_P << "Canvas: found a shape of an invalid type " << type
1559  << ".\n";
1560 
1561  assert(false);
1562  }
1563  }
1564 }
1565 
1566 /***** ***** ***** ***** ***** SHAPE ***** ***** ***** ***** *****/
1567 
1568 } // namespace gui2
1569 
1570 /*WIKI
1571  * @page = GUICanvasWML
1572  * @order = ZZZZZZ_footer
1573  *
1574  * [[Category: WML Reference]]
1575  * [[Category: GUI WML Reference]]
1576  *
1577  */
1578 
1579 /*WIKI
1580  * @page = GUIVariable
1581  * @order = ZZZZZZ_footer
1582  *
1583  * [[Category: WML Reference]]
1584  * [[Category: GUI WML Reference]]
1585  */
Define the common log macros for the gui toolkit.
#define ERR_GUI_E
Definition: log.hpp:38
unsigned decode_font_style(const std::string &style)
Converts a font style string to a font style.
Definition: helper.cpp:55
#define DBG_GUI_P
Definition: log.hpp:69
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
unsigned blur_depth_
The depth of the blur to use in the pre committing.
Definition: canvas.hpp:155
surface flip_surface(const surface &surf, bool optimize)
Definition: utils.cpp:2108
tformula< unsigned > x_
The x coordinate of the rectangle.
Definition: canvas.cpp:682
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:150
ttext & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:524
tformula< int > maximum_height_
The maximum height for the text.
Definition: canvas.cpp:1286
tformula< PangoAlignment > text_alignment_
The alignment of the text.
Definition: canvas.cpp:1262
family_class
Font classes for get_font_families().
ttext & set_link_aware(bool b)
Definition: text.cpp:546
GLenum GLenum GLenum GLenum GLenum scale
Definition: glew.h:10669
unsigned w_
Width of the canvas.
Definition: canvas.hpp:158
Graphical text output.
ttext & set_font_style(const unsigned font_style)
Definition: text.cpp:418
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
void put_pixel(const surface &surf, surface_lock &surf_lock, int x, int y, Uint32 pixel)
Helper methods for setting/getting a single pixel in an image.
Definition: utils.cpp:1975
surface stretch_surface_vertical(const surface &surf, const unsigned h, const bool optimize)
Stretches a surface in the vertical direction.
Definition: utils.cpp:223
surface image_
The image is cached in this surface.
Definition: canvas.cpp:961
tformula< unsigned > x1_
The start x coordinate of the line.
Definition: canvas.cpp:278
tformula< std::string > image_name_
Name of the image.
Definition: canvas.cpp:972
ttext & set_font_size(const unsigned font_size)
Definition: text.cpp:406
#define h
tformula< unsigned > h_
The height of the rectangle.
Definition: canvas.cpp:682
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
surface scale_surface(const surface &surf, int w, int h)
Definition: utils.cpp:443
ttext & set_maximum_width(int width)
Definition: text.cpp:446
#define d
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2335
ttext & set_link_color(const std::string &color)
Definition: text.cpp:556
#define VALIDATE_WITH_DEV_MESSAGE(cond, message, dev_message)
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
tformula< t_string > text_
The text to draw.
Definition: canvas.cpp:1268
-file util.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
unsigned thickness_
The thickness of the line.
Definition: canvas.cpp:293
variant query_value(const std::string &key) const
Definition: callable.hpp:39
#define ERR_GUI_P
Definition: log.hpp:72
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2185
#define DBG_GUI_D
Definition: log.hpp:29
unsigned h_
Height of the canvas.
Definition: canvas.hpp:161
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
tformula< unsigned > w_
The width of the rectangle.
Definition: canvas.cpp:682
lg::log_domain log_gui_parse("gui/parse")
Definition: log.hpp:68
surface canvas_
The surface we draw all items on.
Definition: canvas.hpp:164
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
void draw_solid_tinted_rectangle(int x, int y, int w, int h, int r, int g, int b, double alpha, surface target)
Fills a specified rectangle area of a surface with a given color and opacity.
Definition: rect.cpp:117
std::ostringstream wrapper.
Definition: formatter.hpp:32
unsigned font_style_
The style of the text.
Definition: canvas.cpp:1259
ttext & set_maximum_height(int height, bool multiline)
Definition: text.cpp:491
Uint32 border_color_
The border color of the rectangle.
Definition: canvas.cpp:699
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
int as_int() const
Definition: variant.cpp:558
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
GLenum GLenum dst
Definition: glew.h:2392
unsigned font_size_
The font size of the text.
Definition: canvas.cpp:1256
GLuint start
Definition: glew.h:1221
This file contains the canvas object which is the part where the widgets draw (temporally) images on...
bool is_dirty_
The dirty state of the canvas.
Definition: canvas.hpp:170
void draw(const bool force=false)
Draws the canvas.
Definition: canvas.cpp:1467
void draw(surface screen)
#define log_scope2(domain, description)
Definition: log.hpp:186
ttext & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:479
Helper class for pinning SDL surfaces into memory.
Definition: utils.hpp:439
void parse_cfg(const config &cfg)
Parses a config object.
Definition: canvas.cpp:1521
tformula< unsigned > y2_
The end y coordinate of the line.
Definition: canvas.cpp:278
#define ERR_GUI_D
Definition: log.hpp:32
surface blur_surface(const surface &surf, int depth, bool optimize)
Cross-fades a surface.
Definition: utils.cpp:1524
unsigned border_thickness_
Border thickness.
Definition: canvas.cpp:692
surf
Definition: filter.cpp:143
GLuint color
Definition: glew.h:5801
lu_byte right
Definition: lparser.cpp:1020
void blit(surface &surf, SDL_Rect rect)
Blits the canvas unto another surface.
Definition: canvas.cpp:1497
Uint32 fill_color_
The border color of the rectangle.
Definition: canvas.cpp:706
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
GLint left
Definition: glew.h:5907
bool has_key(const std::string &key) const
Definition: callable.hpp:71
surface render() const
Returns the rendered text.
Definition: text.cpp:166
logger & err()
Definition: log.cpp:79
#define WRN_GUI_D
Definition: log.hpp:31
font::family_class font_family_
The text font family.
Definition: canvas.cpp:1253
std::vector< tshape_ptr > shapes_
Vector with the shapes to draw.
Definition: canvas.hpp:145
SDL_Rect src_clip_
Contains the size of the image.
Definition: canvas.cpp:958
Uint32 color_
The color of the line.
Definition: canvas.cpp:284
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
tformula< bool > link_aware_
The link aware switch of the text.
Definition: canvas.cpp:1274
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
tformula< unsigned > y_
The y coordinate of the rectangle.
Definition: canvas.cpp:682
map_formula_callable & add(const std::string &key, const variant &value)
Definition: formula.cpp:41
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
#define debug(x)
lg::log_domain log_gui_draw("gui/draw")
Definition: log.hpp:28
bool is_neutral(const surface &surf)
Check that the surface is neutral bpp 32.
Definition: utils.cpp:113
boost::uint32_t decode_color(const std::string &color)
Converts a color string to a color.
Definition: helper.cpp:73
GLuint const GLchar * name
Definition: glew.h:1782
surface & get_video_surface()
Definition: dummy_video.cpp:36
unsigned characters_per_line_
The number of characters per line.
Definition: canvas.cpp:1283
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
tformula< int > maximum_width_
The maximum width for the text.
Definition: canvas.cpp:1280
tformula< unsigned > radius_
The radius of the circle.
Definition: canvas.cpp:842
tformula< unsigned > y1_
The start y coordinate of the line.
Definition: canvas.cpp:278
tformula< bool > text_markup_
The text markup switch of the text.
Definition: canvas.cpp:1271
Contains the SDL_Rect helper code.
tresize_mode resize_mode_
The resize mode for an image.
Definition: canvas.cpp:990
tformula< std::string > link_color_
The link color of the text.
Definition: canvas.cpp:1277
void swap(game_board &one, game_board &other)
Definition: game_board.cpp:56
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:135
ttext & set_family_class(font::family_class fclass)
Definition: text.cpp:395
tformula< unsigned > x2_
The end x coordinate of the line.
Definition: canvas.cpp:278
void assign(const surface &o)
Definition: utils.hpp:83
GLint GLint bottom
Definition: glew.h:5907
tformula< bool > vertical_mirror_
Mirror the image over the vertical axis.
Definition: canvas.cpp:993
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:360
family_class str_to_family_class(const std::string &str)
void get_screen_size_variables(game_logic::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:132
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
GLdouble s
Definition: glew.h:1358
surface stretch_surface_horizontal(const surface &surf, const unsigned w, const bool optimize)
Stretches a surface in the horizontal direction.
Definition: utils.cpp:178
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Text class.
Definition: text.hpp:66
ttext & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:510
game_logic::map_formula_callable variables_
The variables of the canvas.
Definition: canvas.hpp:167
ttext & set_foreground_color(const Uint32 color)
Definition: text.cpp:429