gui/auxiliary/canvas.cpp

Go to the documentation of this file.
00001 /* $Id: canvas.cpp 54038 2012-04-30 19:37:24Z mordante $ */
00002 /*
00003    Copyright (C) 2007 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Implementation of canvas.hpp.
00019  */
00020 
00021 #define GETTEXT_DOMAIN "wesnoth-lib"
00022 
00023 #include "gui/auxiliary/canvas.hpp"
00024 
00025 #include "config.hpp"
00026 #include "../../image.hpp"
00027 #include "foreach.hpp"
00028 #include "formatter.hpp"
00029 #include "gettext.hpp"
00030 #include "gui/auxiliary/formula.hpp"
00031 #include "gui/auxiliary/log.hpp"
00032 #include "gui/widgets/helper.hpp"
00033 #include "../../text.hpp"
00034 #include "wml_exception.hpp"
00035 
00036 namespace gui2 {
00037 
00038 namespace {
00039 
00040 /*WIKI
00041  * @page = GUICanvasWML
00042  *
00043  * {{Autogenerated}}
00044  *
00045  * = Canvas =
00046  *
00047  * A canvas is a blank drawing area on which the user can draw several shapes.
00048  * The drawing is done by adding WML structures to the canvas.
00049  */
00050 
00051 /*WIKI
00052  * @page = GUICanvasWML
00053  * @begin{parent}{name="generic/state/draw/"}
00054  *
00055  * == Pre commit ==
00056  * @begin{tag}{name="pre_commit"}{min="0"}{max="1"}
00057  *
00058  * This section contains the pre commit functions. These functions will be
00059  * executed before the drawn canvas is applied on top of the normal
00060  * background. There should only be one pre commit section and its order
00061  * regarding the other shapes doesn't matter. The function has effect on the
00062  * entire canvas, it's not possible to affect only a small part of the canvas.
00063  *
00064  * The section can have one of the following subsections.
00065  *
00066  * === Blur ===
00067  * @begin{tag}{name="blur"}{min="0"}{max="1"}
00068  *
00069  * Blurs the background before applying the canvas. This doesn't make sense
00070  * if the widget isn't semi-transparent.
00071  *
00072  * Keys:
00073  * @begin{table}{config}
00074  *     depth & unsigned & 0 &            The depth to blur. $
00075  * @end{table}
00076  * @end{tag}{name="blur"}
00077  * @end{tag}{name="pre_commit"}
00078  */
00079 
00080 /***** ***** ***** ***** ***** DRAWING PRIMITIVES ***** ***** ***** ***** *****/
00081 
00082 /**
00083  * Draws a single pixel on a surface.
00084  *
00085  * @pre                   The caller needs to make sure the selected coordinate
00086  *                        fits on the @p surface.
00087  * @pre                   The @p canvas is locked.
00088  *
00089  * @param start           The memory address which is the start of the surface
00090  *                        buffer to draw in.
00091  * @param color           The color of the pixel to draw.
00092  * @param w               The width of the surface.
00093  * @param x               The x coordinate of the pixel to draw.
00094  * @param y               The y coordinate of the pixel to draw.
00095  */
00096 static void put_pixel(
00097           const ptrdiff_t start
00098         , const Uint32 color
00099         , const unsigned w
00100         , const unsigned x
00101         , const unsigned y)
00102 {
00103     *reinterpret_cast<Uint32*>(start + (y * w * 4) + x * 4) = color;
00104 }
00105 
00106 /**
00107  * Draws a line on a surface.
00108  *
00109  * @pre                   The caller needs to make sure the entire line fits on
00110  *                        the @p surface.
00111  * @pre                   @p x2 >= @p x1
00112  * @pre                   The @p surface is locked.
00113  *
00114  * @param canvas          The canvas to draw upon, the caller should lock the
00115  *                        surface before calling.
00116  * @param color           The color of the line to draw.
00117  * @param x1              The start x coordinate of the line to draw.
00118  * @param y1              The start y coordinate of the line to draw.
00119  * @param x2              The end x coordinate of the line to draw.
00120  * @param y2              The end y coordinate of the line to draw.
00121  */
00122 static void draw_line(
00123           surface& canvas
00124         , Uint32 color
00125         , unsigned x1
00126         , unsigned y1
00127         , const unsigned x2
00128         , unsigned y2)
00129 {
00130     color = SDL_MapRGBA(canvas->format,
00131         ((color & 0xFF000000) >> 24),
00132         ((color & 0x00FF0000) >> 16),
00133         ((color & 0x0000FF00) >> 8),
00134         ((color & 0x000000FF)));
00135 
00136     ptrdiff_t start = reinterpret_cast<ptrdiff_t>(canvas->pixels);
00137     unsigned w = canvas->w;
00138 
00139     DBG_GUI_D << "Shape: draw line from "
00140             << x1 << ',' << y1 << " to " << x2 << ',' << y2
00141             << " canvas width " << w << " canvas height "
00142             << canvas->h << ".\n";
00143 
00144     assert(static_cast<int>(x1) < canvas->w);
00145     assert(static_cast<int>(x2) < canvas->w);
00146     assert(static_cast<int>(y1) < canvas->h);
00147     assert(static_cast<int>(y2) < canvas->h);
00148 
00149     // use a special case for vertical lines
00150     if(x1 == x2) {
00151         if(y2 < y1) {
00152             std::swap(y1, y2);
00153         }
00154 
00155         for(unsigned y = y1; y <= y2; ++y) {
00156             put_pixel(start, color, w, x1, y);
00157         }
00158         return;
00159     }
00160 
00161     // use a special case for horizontal lines
00162     if(y1 == y2) {
00163         for(unsigned x  = x1; x <= x2; ++x) {
00164             put_pixel(start, color, w, x, y1);
00165         }
00166         return;
00167     }
00168 
00169     // Algorithm based on
00170     // http://de.wikipedia.org/wiki/Bresenham-Algorithmus#Kompakte_Variante
00171     // version of 26.12.2010.
00172     const int dx = x2 - x1; // precondition x2 >= x1
00173     const int dy = abs(static_cast<int>(y2 - y1));
00174     const int step_x = 1;
00175     const int step_y = y1 < y2 ? 1 : -1;
00176     int err = (dx > dy ? dx : -dy) / 2;
00177     int e2;
00178 
00179     for(;;){
00180         put_pixel(start, color, w, x1, y1);
00181         if(x1 == x2 && y1 == y2) {
00182             break;
00183         }
00184         e2 = err;
00185         if(e2 > -dx) {
00186             err -= dy;
00187             x1 += step_x;
00188         }
00189         if(e2 <  dy) {
00190             err += dx;
00191             y1 += step_y;
00192         }
00193     }
00194 }
00195 
00196 /**
00197  * Draws a circle on a surface.
00198  *
00199  * @pre                   The circle must fit on the canvas.
00200  * @pre                   The @p surface is locked.
00201  *
00202  * @param canvas          The canvas to draw upon, the caller should lock the
00203  *                        surface before calling.
00204  * @param color           The color of the circle to draw.
00205  * @param x_centre        The x coordinate of the centre of the circle to draw.
00206  * @param y_centre        The y coordinate of the centre of the circle to draw.
00207  * @param radius          The radius of the circle to draw.
00208  */
00209 static void draw_circle(
00210           surface& canvas
00211         , Uint32 color
00212         , const unsigned x_centre
00213         , const unsigned y_centre
00214         , const unsigned radius)
00215 {
00216     color = SDL_MapRGBA(canvas->format,
00217         ((color & 0xFF000000) >> 24),
00218         ((color & 0x00FF0000) >> 16),
00219         ((color & 0x0000FF00) >> 8),
00220         ((color & 0x000000FF)));
00221 
00222     ptrdiff_t start = reinterpret_cast<ptrdiff_t>(canvas->pixels);
00223     unsigned w = canvas->w;
00224 
00225     DBG_GUI_D << "Shape: draw circle at "
00226             << x_centre << ',' << y_centre
00227             << " with radius " << radius
00228             << " canvas width " << w << " canvas height "
00229             << canvas->h << ".\n";
00230 
00231     assert(static_cast<int>(x_centre + radius) < canvas->w);
00232     assert(static_cast<int>(x_centre - radius) >= 0);
00233     assert(static_cast<int>(y_centre + radius) < canvas->h);
00234     assert(static_cast<int>(y_centre - radius) >= 0);
00235 
00236     // Algorithm based on
00237     // http://de.wikipedia.org/wiki/Rasterung_von_Kreisen#Methode_von_Horn
00238     // version of 2011.02.07.
00239     int d = -static_cast<int>(radius);
00240     int x = radius;
00241     int y = 0;
00242     while(!(y > x)) {
00243         put_pixel(start, color, w, x_centre + x, y_centre + y);
00244         put_pixel(start, color, w, x_centre + x, y_centre - y);
00245         put_pixel(start, color, w, x_centre - x, y_centre + y);
00246         put_pixel(start, color, w, x_centre - x, y_centre - y);
00247 
00248         put_pixel(start, color, w, x_centre + y, y_centre + x);
00249         put_pixel(start, color, w, x_centre + y, y_centre - x);
00250         put_pixel(start, color, w, x_centre - y, y_centre + x);
00251         put_pixel(start, color, w, x_centre - y, y_centre - x);
00252 
00253         d += 2 * y + 1;
00254         ++y;
00255         if(d > 0) {
00256             d += -2 * x + 2;
00257             --x;
00258         }
00259     }
00260 }
00261 
00262 /***** ***** ***** ***** ***** LINE ***** ***** ***** ***** *****/
00263 
00264 /** Definition of a line shape. */
00265 class tline
00266     : public tcanvas::tshape
00267 {
00268 public:
00269 
00270     /**
00271      * Constructor.
00272      *
00273      * @param cfg                 The config object to define the line see
00274      *                            http://www.wesnoth.org/wiki/GUICanvasWML#Line
00275      *                            for more information.
00276      */
00277     explicit tline(const config& cfg);
00278 
00279     /** Implement shape::draw(). */
00280     void draw(surface& canvas
00281             , const game_logic::map_formula_callable& variables);
00282 
00283 private:
00284     tformula<unsigned>
00285         x1_, /**< The start x coordinate of the line. */
00286         y1_, /**< The start y coordinate of the line. */
00287         x2_, /**< The end x coordinate of the line. */
00288         y2_; /**< The end y coordinate of the line. */
00289 
00290     /** The color of the line. */
00291     Uint32 color_;
00292 
00293     /**
00294      * The thickness of the line.
00295      *
00296      * if the value is odd the x and y are the middle of the line.
00297      * if the value is even the x and y are the middle of a line
00298      * with width - 1. (0 is special case, does nothing.)
00299      */
00300     unsigned thickness_;
00301 };
00302 
00303 tline::tline(const config& cfg)
00304     : x1_(cfg["x1"])
00305     , y1_(cfg["y1"])
00306     , x2_(cfg["x2"])
00307     , y2_(cfg["y2"])
00308     , color_(decode_color(cfg["color"]))
00309     , thickness_(cfg["thickness"])
00310 {
00311 /*WIKI
00312  * @page = GUICanvasWML
00313  *
00314  * == Line ==
00315  * @begin{tag}{name="line"}{min="0"}{max="-1"}
00316  * Definition of a line. When drawing a line it doesn't get blended on the
00317  * surface but replaces the pixels instead. A blitting flag might be added later
00318  * if needed.
00319  *
00320  * Keys:
00321  * @begin{table}{config}
00322  *     x1 & f_unsigned & 0 &           The x coordinate of the startpoint. $
00323  *     y1 & f_unsigned & 0 &           The y coordinate of the startpoint. $
00324  *     x2 & f_unsigned & 0 &           The x coordinate of the endpoint. $
00325  *     y2 & f_unsigned & 0 &           The y coordinate of the endpoint. $
00326  *     color & color & "" &            The color of the line. $
00327  *     thickness & unsigned & 0 &      The thickness of the line if 0 nothing
00328  *                                     is drawn. $
00329  *     debug & string & "" &           Debug message to show upon creation
00330  *                                     this message is not stored. $
00331  * @end{table}
00332  * @end{tag}{name="line"}
00333  *
00334  * <span id="general_variables">Variables:</span>.
00335  * @begin{table}{formula}
00336  *     width & unsigned &                 The width of the canvas. $
00337  *     height & unsigned &                The height of the canvas. $
00338  *     text & tstring &                   The text to render on the widget. $
00339  *     text_maximum_width & unsigned &    The maximum width available for the text
00340  *                                        on the widget. $
00341  *     text_maximum_height & unsigned &   The maximum height available for the text
00342  *                                        on the widget. $
00343  *     text_wrap_mode & int  &            When the text doesn't fit in the
00344  *                                        available width there are several ways
00345  *                                        to fix that. This variable holds the
00346  *                                        best method. (NOTE this is a 'hidden'
00347  *                                        variable meant to copy state from a
00348  *                                        widget to its canvas so there's no
00349  *                                        reason to use this variable and thus
00350  *                                        its values are not listed and might
00351  *                                        change without further notice.) $
00352  *     text_alignment & h_align &         The way the text is aligned inside the
00353  *                                        canvas. $
00354  *@end{table}
00355  *
00356  * The size variables are copied to the window and will be determined at
00357  * runtime. This is needed since the main window can be resized and the dialog
00358  * needs to resize accordingly. The following variables are available:
00359  * @begin{table}{formula}
00360  *     screen_width & unsigned &        The usable width of the Wesnoth main
00361  *                                      window. $
00362  *     screen_height & unsigned &       The usable height of the Wesnoth main
00363  *                                      window. $
00364  *     gamemap_width & unsigned &       The usable width of the Wesnoth gamemap,
00365  *                                      if no gamemap shown it's the same value as
00366  *                                      screen_width. $
00367  *     gamemap_height & unsigned &      The usable height of the Wesnoth gamemap,
00368  *                                      if no gamemap shown it's the same value as
00369  *                                      screen_height. $
00370  * @end{table}
00371  *
00372  * Note when drawing the valid coordinates are:<br>
00373  * 0 -> width - 1 <br>
00374  * 0 -> height -1
00375  *
00376  * Drawing outside this area will result in unpredictable results including
00377  * crashing. (That should be fixed, when encountered.)
00378  */
00379 
00380 /*WIKI - unclassified
00381  * This code can be used by a parser to generate the wiki page
00382  * structure
00383  * [tag name]
00384  * param type_info description
00385  *
00386  * param                               Name of the parameter.
00387  *
00388  * type_info = ( type = default_value) The info about a optional parameter.
00389  * type_info = ( type )                The info about a mandatory parameter
00390  * type_info = [ type_info ]           The info about a conditional parameter
00391  *                                     description should explain the reason.
00392  *
00393  * description                         Description of the parameter.
00394  *
00395  *
00396  *
00397  *
00398  * Formulas are a function between brackets, that way the engine can see whether
00399  * there is standing a plain number or a formula eg:
00400  * 0     A value of zero
00401  * (0)   A formula returning zero
00402  *
00403  * When formulas are available the text should state the available variables
00404  * which are available in that function.
00405  */
00406 
00407 /*WIKI
00408  * @page = GUIVariable
00409  *
00410  * {{Autogenerated}}
00411  *
00412  * = Variables =
00413  *
00414  * In various parts of the GUI there are several variables types in use. This
00415  * page describes them.
00416  *
00417  * == Simple types ==
00418  *
00419  * The simple types are types which have one value or a short list of options.
00420  *
00421  * @begin{table}{variable_types}
00422  *     unsigned &                      Unsigned number (positive whole numbers
00423  *                                     and zero). $
00424  *     f_unsigned &                    Unsigned number or formula returning an
00425  *                                     unsigned number. $
00426  *     int &                           Signed number (whole numbers). $
00427  *     f_int &                         Signed number or formula returning an
00428  *                                     signed number. $
00429  *     bool &                          A boolean value accepts the normal
00430  *                                     values as the rest of the game. $
00431  *     f_bool &                        Boolean value or a formula returning a
00432  *                                     boolean value. $
00433  *     string &                        A text. $
00434  *     tstring &                       A translatable string. $
00435  *     f_tstring &                     Formula returning a translatable string. $
00436  *
00437  *     color &                         A string which contains the color, this
00438  *                                     a group of 4 numbers between 0 and 255
00439  *                                     separated by a comma. The numbers are red
00440  *                                     component, green component, blue
00441  *                                     component and alpha. A color of 0 is not
00442  *                                     available. An alpha of 255 is fully
00443  *                                     transparent. Omitted values are set to 0. $
00444  *
00445  *     font_style &                    A string which contains the style of the
00446  *                                     font:
00447  *                                     @* normal    normal font
00448  *                                     @* bold      bold font
00449  *                                     @* italic    italic font
00450  *                                     @* underline underlined font
00451  *                                     @-Since SDL has problems combining these
00452  *                                     styles only one can be picked. Once SDL
00453  *                                     will allow multiple options, this type
00454  *                                     will be transformed to a comma separated
00455  *                                     list. If empty we default to the normal
00456  *                                     style. Since the render engine is
00457  *                                     replaced by Pango markup this field will
00458  *                                     change later on. Note widgets that allow
00459  *                                     marked up text can use markup to change
00460  *                                     the font style. $
00461  *
00462  *     v_align &                       Vertical alignment; how an item is
00463  *                                     aligned vertically in the available
00464  *                                     space. Possible values:
00465  *                                     @* top    aligned at the top
00466  *                                     @* bottom aligned at the bottom
00467  *                                     @* center centered
00468  *                                     @-When nothing is set or an another
00469  *                                     value as in the list the item is
00470  *                                     centered. $
00471  *
00472  *     h_align &                       Horizontal alignment; how an item is
00473  *                                     aligned horizontal in the available
00474  *                                     space. Possible values:
00475  *                                     @* left   aligned at the left side
00476  *                                     @* right  aligned at the right side
00477  *                                     @* center centered $
00478  *
00479  *     f_h_align &                     A horizontal alignment or a formula
00480  *                                     returning a horizontal alignment. $
00481  *
00482  *     border &                        Comma separated list of borders to use.
00483  *                                     Possible values:
00484  *                                     @* left   border at the left side
00485  *                                     @* right  border at the right side
00486  *                                     @* top    border at the top
00487  *                                     @* bottom border at the bottom
00488  *                                     @* all    alias for "left, right, top,
00489  *                                     bottom" $
00490  *
00491  *     scrollbar_mode &                How to show the scrollbar of a widget.
00492  *                                     Possible values:
00493  *                                     @* always       The scrollbar is always
00494  *                                     shown, regardless whether it's required
00495  *                                     or not.
00496  *                                     @* never        The scrollbar is never
00497  *                                     shown, even not when needed. (Note when
00498  *                                     setting this mode dialogs might
00499  *                                     not properly fit anymore).
00500  *                                     @* auto         Shows the scrollbar when
00501  *                                     needed. The widget will reserve space for
00502  *                                     the scrollbar, but only show when needed.
00503  *                                     @* initial_auto Like auto, but when the
00504  *                                     scrollbar is not needed the space is not
00505  *                                     reserved.
00506  *                                     @-Use auto when the list can be changed
00507  *                                     dynamically eg the game list in the
00508  *                                     lobby. For optimization you can also
00509  *                                     use auto when you really expect a
00510  *                                     scrollbar, but don't want it to be shown
00511  *                                     when not needed eg the language list
00512  *                                     will need a scrollbar on most screens. $
00513  *
00514  *     resize_mode &                   Determines how an image is resized.
00515  *                                     Possible values:
00516  *                                     @* scale        The image is scaled.
00517  *                                     @* stretch      The first row or column
00518  *                                     of pixels is copied over the entire
00519  *                                     image. (Can only be used to scale resize
00520  *                                     in one direction, else falls
00521  *                                     back to scale.)
00522  *                                     @* tile         The image is placed
00523  *                                     several times until the entire surface
00524  *                                     is filled. The last images are
00525  *                                     truncated. $
00526  * @end{table}
00527  * @allow{type}{name="unsigned"}{value="^\d+$"}
00528  * @allow{type}{name="f_unsigned"}{value="^.+$"}
00529  * @allow{type}{name="int"}{value="^-?\d+$"}
00530  * @allow{type}{name="f_int"}{value="^.*$"}
00531  * @allow{type}{name="bool"}{value="^true|false|yes|no$"}
00532  * @allow{type}{name="f_bool"}{value="^.*$"}
00533  * @allow{type}{name="string"}{value="^.*$"}
00534  * @allow{type}{name="t_string"}{value="^_?.*$"}
00535  * @allow{type}{name="f_string"}{value="^.*$"}
00536  * @allow{type}{name="f_tstring"}{value="^_?.*$"}
00537  *
00538  * @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)$"}
00539  *
00540  * @allow{type}{name="font_style"}{value="^(normal|bold|italic|underline)?$"}
00541  * @allow{type}{name="v_align"}{value="^top|bottom|center$"}
00542  * @allow{type}{name="h_align"}{value="^left|right|center$"}
00543  * @allow{type}{name="f_h_align"}{value="^.*$"}
00544  * @allow{type}{name="border"}{value="^(top|bottom|left|right|all)?(,\s*(top|bottom|left|right|all))*$"}
00545  * @allow{type}{name="scrollbar_mode"}{value="^always|never|auto|initial_auto$"}
00546  * @allow{type}{name="resize_mode"}{value="^scale|stretch|tile$"}
00547  *
00548  * @remove{type}{name="section"}
00549  * @remove{type}{name="config"}
00550  * @remove{type}{name="grid"}
00551  * == Section types ==
00552  *
00553  * For more complex parts, there are sections. Sections contain of several
00554  * lines of WML and can have sub sections. For example a grid has sub sections
00555  * which contain various widgets. Here's the list of sections.
00556  *
00557  * @begin{table}{variable_types}
00558  *     section &                       A generic section. The documentation
00559  *                                     about the section should describe the
00560  *                                     section in further detail. $
00561  *
00562  *     grid &                          A grid contains several widgets. (TODO
00563  *                                     add link to generic grid page.) $
00564  * @end{table}
00565  */
00566 
00567     const std::string& debug = (cfg["debug"]);
00568     if(!debug.empty()) {
00569         DBG_GUI_P << "Line: found debug message '" << debug << "'.\n";
00570     }
00571 }
00572 
00573 void tline::draw(surface& canvas
00574         , const game_logic::map_formula_callable& variables)
00575 {
00576     /**
00577      * @todo formulas are now recalculated every draw cycle which is a bit silly
00578      * unless there has been a resize. So to optimize we should use an extra
00579      * flag or do the calculation in a separate routine.
00580      */
00581 
00582     const unsigned x1 = x1_(variables);
00583     const unsigned y1 = y1_(variables);
00584     const unsigned x2 = x2_(variables);
00585     const unsigned y2 = y2_(variables);
00586 
00587     DBG_GUI_D << "Line: draw from "
00588             << x1 << ',' << y1 << " to " << x2 << ',' << y2
00589             << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00590 
00591     VALIDATE(
00592               static_cast<int>(x1) < canvas->w
00593                 && static_cast<int>(x2) < canvas->w
00594                 && static_cast<int>(y1) < canvas->h
00595                 && static_cast<int>(y2) < canvas->h
00596             , _("Line doesn't fit on canvas."));
00597 
00598     // @todo FIXME respect the thickness.
00599 
00600     // now draw the line we use Bresenham's algorithm, which doesn't
00601     // support antialiasing. The advantage is that it's easy for testing.
00602 
00603     // lock the surface
00604     surface_lock locker(canvas);
00605     if(x1 > x2) {
00606         // invert points
00607         draw_line(canvas, color_, x2, y2, x1, y1);
00608     } else {
00609         draw_line(canvas, color_, x1, y1, x2, y2);
00610     }
00611 }
00612 
00613 /***** ***** ***** ***** ***** Rectangle ***** ***** ***** ***** *****/
00614 
00615 /** Definition of a rectangle shape. */
00616 class trectangle
00617     : public tcanvas::tshape
00618 {
00619 public:
00620 
00621     /**
00622      * Constructor.
00623      *
00624      * @param cfg                 The config object to define the rectangle see
00625      *                            http://www.wesnoth.org/wiki/GUICanvasWML#Rectangle
00626      *                            for more information.
00627      */
00628     explicit trectangle(const config& cfg);
00629 
00630     /** Implement shape::draw(). */
00631     void draw(surface& canvas
00632             , const game_logic::map_formula_callable& variables);
00633 
00634 private:
00635     tformula<unsigned>
00636         x_, /**< The x coordinate of the rectangle. */
00637         y_, /**< The y coordinate of the rectangle. */
00638         w_, /**< The width of the rectangle. */
00639         h_; /**< The height of the rectangle. */
00640 
00641     /**
00642      * Border thickness.
00643      *
00644      * If 0 the fill color is used for the entire widget.
00645      */
00646     unsigned border_thickness_;
00647 
00648     /**
00649      * The border color of the rectangle.
00650      *
00651      * If the color is fully transparent the border isn't drawn.
00652      */
00653     Uint32 border_color_;
00654 
00655     /**
00656      * The border color of the rectangle.
00657      *
00658      * If the color is fully transparent the rectangle won't be filled.
00659      */
00660     Uint32 fill_color_;
00661 };
00662 
00663 trectangle::trectangle(const config& cfg)
00664     : x_(cfg["x"])
00665     , y_(cfg["y"])
00666     , w_(cfg["w"])
00667     , h_(cfg["h"])
00668     , border_thickness_(cfg["border_thickness"])
00669     , border_color_(decode_color(cfg["border_color"]))
00670     , fill_color_(decode_color(cfg["fill_color"]))
00671 {
00672 /*WIKI
00673  * @page = GUICanvasWML
00674  *
00675  * == Rectangle ==
00676  * @begin{tag}{name="rectangle"}{min="0"}{max="-1"}
00677  *
00678  * Definition of a rectangle. When drawing a rectangle it doesn't get blended on
00679  * the surface but replaces the pixels instead. A blitting flag might be added
00680  * later if needed.
00681  *
00682  * Keys:
00683  * @begin{table}{config}
00684  *     x & f_unsigned & 0 &            The x coordinate of the top left corner. $
00685  *     y & f_unsigned & 0 &            The y coordinate of the top left corner. $
00686  *     w & f_unsigned & 0 &            The width of the rectangle. $
00687  *     h & f_unsigned & 0 &            The height of the rectangle. $
00688  *     border_thickness & unsigned & 0 &
00689  *                                     The thickness of the border if the
00690  *                                     thickness is zero it's not drawn. $
00691  *     border_color & color & "" &     The color of the border if empty it's
00692  *                                     not drawn. $
00693  *     fill_color & color & "" &       The color of the interior if omitted
00694  *                                     it's not drawn. $
00695  *     debug & string & "" &           Debug message to show upon creation
00696  *                                     this message is not stored. $
00697  * @end{table}
00698  * @end{tag}{name="rectangle"}
00699  * Variables:
00700  * See [[#general_variables|Line]].
00701  *
00702  */
00703     if(border_color_ == 0) {
00704         border_thickness_ = 0;
00705     }
00706 
00707     const std::string& debug = (cfg["debug"]);
00708     if(!debug.empty()) {
00709         DBG_GUI_P << "Rectangle: found debug message '" << debug << "'.\n";
00710     }
00711 }
00712 
00713 void trectangle::draw(surface& canvas
00714         , const game_logic::map_formula_callable& variables)
00715 {
00716     /**
00717      * @todo formulas are now recalculated every draw cycle which is a  bit
00718      * silly unless there has been a resize. So to optimize we should use an
00719      * extra flag or do the calculation in a separate routine.
00720      */
00721     const unsigned x = x_(variables);
00722     const unsigned y = y_(variables);
00723     const unsigned w = w_(variables);
00724     const unsigned h = h_(variables);
00725 
00726     DBG_GUI_D << "Rectangle: draw from " << x << ',' << y
00727             << " width " << w << " height " << h
00728             << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00729 
00730     VALIDATE(
00731               static_cast<int>(x) < canvas->w
00732                 && static_cast<int>(x + w) <= canvas->w
00733                 && static_cast<int>(y) < canvas->h
00734                 && static_cast<int>(y + h) <= canvas->h
00735             , _("Rectangle doesn't fit on canvas."));
00736 
00737 
00738     surface_lock locker(canvas);
00739 
00740     // draw the border
00741     for(unsigned i = 0; i < border_thickness_; ++i) {
00742 
00743         const unsigned left = x + i;
00744         const unsigned right = left + w - (i * 2) - 1;
00745         const unsigned top = y + i;
00746         const unsigned bottom = top + h - (i * 2) - 1;
00747 
00748         // top horizontal (left -> right)
00749         draw_line(canvas, border_color_, left, top, right, top);
00750 
00751         // right vertical (top -> bottom)
00752         draw_line(canvas, border_color_, right, top, right, bottom);
00753 
00754         // bottom horizontal (left -> right)
00755         draw_line(canvas, border_color_, left, bottom, right, bottom);
00756 
00757         // left vertical (top -> bottom)
00758         draw_line(canvas, border_color_, left, top, left, bottom);
00759     }
00760 
00761     // The fill_rect_alpha code below fails, can't remember the exact cause
00762     // so use the slow line drawing method to fill the rect.
00763     if(fill_color_) {
00764 
00765         const unsigned left = x + border_thickness_;
00766         const unsigned right = left + w - (2 * border_thickness_) - 1;
00767         const unsigned top = y + border_thickness_;
00768         const unsigned bottom = top + h - (2 * border_thickness_);
00769 
00770         for(unsigned i = top; i < bottom; ++i) {
00771 
00772             draw_line(canvas, fill_color_, left, i, right, i);
00773         }
00774     }
00775 }
00776 
00777 /***** ***** ***** ***** ***** CIRCLE ***** ***** ***** ***** *****/
00778 
00779 /** Definition of a circle shape. */
00780 class tcircle
00781     : public tcanvas::tshape
00782 {
00783 public:
00784 
00785     /**
00786      * Constructor.
00787      *
00788      * @param cfg                 The config object to define the circle see
00789      *                            http://www.wesnoth.org/wiki/GUICanvasWML#Circle
00790      *                            for more information.
00791      */
00792     explicit tcircle(const config& cfg);
00793 
00794     /** Implement shape::draw(). */
00795     void draw(surface& canvas
00796             , const game_logic::map_formula_callable& variables);
00797 
00798 private:
00799     tformula<unsigned>
00800         x_,       /**< The centre x coordinate of the circle. */
00801         y_,       /**< The centre y coordinate of the circle. */
00802         radius_;  /**< The radius of the circle. */
00803 
00804     /** The color of the circle. */
00805     Uint32 color_;
00806 
00807 };
00808 
00809 tcircle::tcircle(const config& cfg)
00810     : x_(cfg["x"])
00811     , y_(cfg["y"])
00812     , radius_(cfg["radius"])
00813     , color_(decode_color(cfg["color"]))
00814 {
00815 /*WIKI
00816  * @page = GUICanvasWML
00817  *
00818  * == Circle ==
00819  * @begin{tag}{name="circle"}{min="0"}{max="-1"}
00820  *
00821  * Definition of a circle. When drawing a circle it doesn't get blended on
00822  * the surface but replaces the pixels instead. A blitting flag might be
00823  * added later if needed.
00824  *
00825  * Keys:
00826  * @begin{table}{config}
00827  * x      & f_unsigned & 0 &       The x coordinate of the centre. $
00828  * y      & f_unsigned & 0 &       The y coordinate of the centre. $
00829  * radius & f_unsigned & 0 &       The radius of the circle if 0 nothing is
00830  *                                 drawn. $
00831  * color & color & "" &            The color of the circle. $
00832  * debug & string & "" &           Debug message to show upon creation this
00833  *                                 message is not stored. $
00834  * @end{table}
00835  * @end{tag}{name="circle"}
00836  * Variables:
00837  * See [[#general_variables|Line]].
00838  *
00839  * Drawing outside the area will result in unpredictable results including
00840  * crashing. (That should be fixed, when encountered.)
00841  */
00842 
00843     const std::string& debug = (cfg["debug"]);
00844     if(!debug.empty()) {
00845         DBG_GUI_P << "Circle: found debug message '" << debug << "'.\n";
00846     }
00847 }
00848 
00849 void tcircle::draw(surface& canvas
00850         , const game_logic::map_formula_callable& variables)
00851 {
00852     /**
00853      * @todo formulas are now recalculated every draw cycle which is a bit
00854      * silly unless there has been a resize. So to optimize we should use an
00855      * extra flag or do the calculation in a separate routine.
00856      */
00857 
00858     const unsigned x = x_(variables);
00859     const unsigned y = y_(variables);
00860     const unsigned radius = radius_(variables);
00861 
00862     DBG_GUI_D << "Circle: drawn at "
00863             << x << ',' << y << " radius " << radius
00864             << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00865 
00866     VALIDATE_WITH_DEV_MESSAGE(
00867              static_cast<int>(x - radius) >= 0
00868             , _("Circle doesn't fit on canvas.")
00869             , (formatter() << "x = " << x << ", radius = " << radius).str());
00870 
00871     VALIDATE_WITH_DEV_MESSAGE(
00872              static_cast<int>(y - radius) >= 0
00873             , _("Circle doesn't fit on canvas.")
00874             , (formatter() << "y = " << y << ", radius = " << radius).str());
00875 
00876     VALIDATE_WITH_DEV_MESSAGE(
00877              static_cast<int>(x + radius) < canvas->w
00878             , _("Circle doesn't fit on canvas.")
00879             , (formatter() << "x = " << x << ", radius = " << radius
00880                 << "', canvas width = " << canvas->w << ".").str());
00881 
00882     VALIDATE_WITH_DEV_MESSAGE(
00883              static_cast<int>(y + radius) < canvas->h
00884             , _("Circle doesn't fit on canvas.")
00885             , (formatter() << "y = " << y << ", radius = " << radius
00886                 << "', canvas height = " << canvas->h << ".").str());
00887 
00888     // lock the surface
00889     surface_lock locker(canvas);
00890     draw_circle(canvas, color_, x, y, radius);
00891 }
00892 
00893 /***** ***** ***** ***** ***** IMAGE ***** ***** ***** ***** *****/
00894 
00895 /** Definition of an image shape. */
00896 class timage
00897     : public tcanvas::tshape
00898 {
00899 public:
00900 
00901     /**
00902      * Constructor.
00903      *
00904      * @param cfg                 The config object to define the image see
00905      *                            http://www.wesnoth.org/wiki/GUICanvasWML#Image
00906      *                            for more infomation.
00907      */
00908     explicit timage(const config& cfg);
00909 
00910     /** Implement shape::draw(). */
00911     void draw(surface& canvas
00912             , const game_logic::map_formula_callable& variables);
00913 
00914 private:
00915     tformula<unsigned>
00916         x_, /**< The x coordinate of the image. */
00917         y_, /**< The y coordinate of the image. */
00918         w_, /**< The width of the image. */
00919         h_; /**< The height of the image. */
00920 
00921     /** Contains the size of the image. */
00922     SDL_Rect src_clip_;
00923 
00924     /** The image is cached in this surface. */
00925     surface image_;
00926 
00927 
00928 
00929     /**
00930      * Name of the image.
00931      *
00932      * This value is only used when the image name is a formula. If it isn't a
00933      * formula the image will be loaded in the constructor. If it's a formula it
00934      * will be loaded every draw cycles. This allows 'changing' images.
00935      */
00936     tformula<std::string> image_name_;
00937 
00938     /**
00939      * Determines the way an image will be resized.
00940      *
00941      * If the image is smaller is needed it needs to resized, how is determined
00942      * by the value of this enum.
00943      */
00944     enum tresize_mode {
00945           scale
00946         , stretch
00947         , tile
00948     };
00949 
00950     /** Converts a string to a resize mode. */
00951     tresize_mode get_resize_mode(const std::string& resize_mode);
00952 
00953     /** The resize mode for an image. */
00954     tresize_mode resize_mode_;
00955 
00956     /** Mirror the image over the vertical axis. */
00957     tformula<bool> vertical_mirror_;
00958 };
00959 
00960 timage::timage(const config& cfg)
00961     : x_(cfg["x"])
00962     , y_(cfg["y"])
00963     , w_(cfg["w"])
00964     , h_(cfg["h"])
00965     , src_clip_()
00966     , image_()
00967     , image_name_(cfg["name"])
00968     , resize_mode_(get_resize_mode(cfg["resize_mode"]))
00969     , vertical_mirror_(cfg["vertical_mirror"])
00970 {
00971 /*WIKI
00972  * @page = GUICanvasWML
00973  *
00974  * == Image ==
00975  * @begin{tag}{name="image"}{min="0"}{max="-1"}
00976  * Definition of an image.
00977  *
00978  * Keys:
00979  * @begin{table}{config}
00980  *     x & f_unsigned & 0 &            The x coordinate of the top left corner. $
00981  *     y & f_unsigned & 0 &            The y coordinate of the top left corner. $
00982  *     w & f_unsigned & 0 &            The width of the image, if not zero the
00983  *                                     image will be scaled to the desired
00984  *                                     width. $
00985  *     h & f_unsigned & 0 &            The height of the image, if not zero the
00986  *                                     image will be scaled to the desired
00987  *                                     height. $
00988  *     resize_mode & resize_mode & scale &
00989  *                                     Determines how an image is scaled to fit
00990  *                                     the wanted size. $
00991  *     vertical_mirror & f_bool & false &
00992  *                                     Mirror the image over the vertical axis. $
00993  *     name & f_string & "" &          The name of the image. $
00994  *     debug & string & "" &           Debug message to show upon creation
00995  *                                     this message is not stored. $
00996  *
00997  * @end{table}
00998  * @end{tag}{name="image"}
00999  * Variables:
01000  * @begin{table}{formula}
01001  *     image_width & unsigned &         The width of the image, either the
01002  *                                      requested width or the natural width of
01003  *                                      the image. This value can be used to set
01004  *                                      the x (or y) value of the image. (This
01005  *                                      means x and y are evaluated after the
01006  *                                      width and height.) $
01007  *     image_height & unsigned &        The height of the image, either the
01008  *                                      requested height or the natural height
01009  *                                      of the image. This value can be used to
01010  *                                      set the y (or x) value of the image.
01011  *                                      (This means x and y are evaluated after
01012  *                                      the width and height.) $
01013  *     image_original_width & unsigned &
01014  *                                      The width of the image as stored on
01015  *                                      disk, can be used to set x or w
01016  *                                      (also y and h can be set). $
01017  *     image_original_height & unsigned &
01018  *                                      The height of the image as stored on
01019  *                                      disk, can be used to set y or h
01020  *                                      (also x and y can be set). $
01021  * @end{table}
01022  * Also the general variables are available, see [[#general_variables|Line]].
01023  */
01024 
01025     const std::string& debug = (cfg["debug"]);
01026     if(!debug.empty()) {
01027         DBG_GUI_P << "Image: found debug message '" << debug << "'.\n";
01028     }
01029 }
01030 
01031 void timage::draw(surface& canvas
01032         , const game_logic::map_formula_callable& variables)
01033 {
01034     DBG_GUI_D << "Image: draw.\n";
01035 
01036     /**
01037      * @todo formulas are now recalculated every draw cycle which is a  bit
01038      * silly unless there has been a resize. So to optimize we should use an
01039      * extra flag or do the calculation in a separate routine.
01040      */
01041     const std::string& name = image_name_(variables);
01042 
01043     if(name.empty()) {
01044         DBG_GUI_D << "Image: formula returned no value, will not be drawn.\n";
01045         return;
01046     }
01047 
01048     /*
01049      * The locator might return a different surface for every call so we can't
01050      * cache the output, also not if no formula is used.
01051      */
01052     surface tmp(image::get_image(image::locator(name)));
01053 
01054     if(!tmp) {
01055         ERR_GUI_D << "Image: '" << name << "' not found and won't be drawn.\n";
01056         return;
01057     }
01058 
01059     image_.assign(make_neutral_surface(tmp));
01060     assert(image_);
01061     src_clip_ = ::create_rect(0, 0, image_->w, image_->h);
01062 
01063     game_logic::map_formula_callable local_variables(variables);
01064     local_variables.add("image_original_width", variant(image_->w));
01065     local_variables.add("image_original_height", variant(image_->h));
01066 
01067     unsigned w = w_(local_variables);
01068     VALIDATE_WITH_DEV_MESSAGE(
01069               static_cast<int>(w) >= 0
01070             , _("Image doesn't fit on canvas.")
01071             , (formatter() << "Image '" << name
01072                 << "', w = " << static_cast<int>(w) << ".").str());
01073 
01074     unsigned h = h_(local_variables);
01075     VALIDATE_WITH_DEV_MESSAGE(
01076               static_cast<int>(h) >= 0
01077             , _("Image doesn't fit on canvas.")
01078             , (formatter() << "Image '" << name
01079                 << "', h = " << static_cast<int>(h) << ".").str());
01080 
01081     local_variables.add("image_width", variant(w ? w : image_->w));
01082     local_variables.add("image_height", variant(h ? h : image_->h));
01083 
01084     const unsigned x = x_(local_variables);
01085     VALIDATE_WITH_DEV_MESSAGE(
01086               static_cast<int>(x) >= 0
01087             , _("Image doesn't fit on canvas.")
01088             , (formatter() << "Image '" << name
01089                 << "', x = " << static_cast<int>(x) << ".").str());
01090 
01091     const unsigned y = y_(local_variables);
01092     VALIDATE_WITH_DEV_MESSAGE(
01093               static_cast<int>(y) >= 0
01094             , _("Image doesn't fit on canvas.")
01095             , (formatter() << "Image '" << name
01096                 << "', y = " << static_cast<int>(y) << ".").str());
01097 
01098     // Copy the data to local variables to avoid overwriting the originals.
01099     SDL_Rect src_clip = src_clip_;
01100     SDL_Rect dst_clip = ::create_rect(x, y, 0, 0);
01101     surface surf;
01102 
01103     // Test whether we need to scale and do the scaling if needed.
01104     if(w || h) {
01105         bool done = false;
01106         bool stretch_image = (resize_mode_ == stretch) && (!!w ^ !!h);
01107         if(!w) {
01108             if(stretch_image) {
01109                 DBG_GUI_D << "Image: vertical stretch from " << image_->w
01110                         << ',' << image_->h << " to a height of " << h << ".\n";
01111 
01112                 surf = stretch_surface_vertical(image_, h, false);
01113                 done = true;
01114             }
01115             w = image_->w;
01116         }
01117 
01118         if(!h) {
01119             if(stretch_image) {
01120                 DBG_GUI_D << "Image: horizontal stretch from " << image_->w
01121                         << ',' << image_->h << " to a width of " << w << ".\n";
01122 
01123                 surf = stretch_surface_horizontal(image_, w, false);
01124                 done = true;
01125             }
01126             h = image_->h;
01127         }
01128 
01129         if(!done) {
01130 
01131             if(resize_mode_ == tile) {
01132                 DBG_GUI_D << "Image: tiling from " << image_->w
01133                         << ',' << image_->h << " to " << w << ',' << h << ".\n";
01134 
01135                 const int columns = (w + image_->w - 1) / image_->w;
01136                 const int rows = (h + image_->h - 1) / image_->h;
01137                 surf = create_neutral_surface(w, h);
01138 
01139                 for(int x = 0; x < columns; ++x) {
01140                     for(int y = 0; y < rows; ++y) {
01141                         const SDL_Rect dest = ::create_rect(
01142                                   x * image_->w
01143                                 , y * image_->h
01144                                 , 0
01145                                 , 0);
01146                         blit_surface(image_, NULL, surf, &dest);
01147                     }
01148                 }
01149 
01150             } else {
01151                 if(resize_mode_ == stretch) {
01152                     ERR_GUI_D << "Image: failed to stretch image, "
01153                             "fall back to scaling.\n";
01154                 }
01155 
01156                 DBG_GUI_D << "Image: scaling from " << image_->w
01157                         << ',' << image_->h << " to " << w << ',' << h << ".\n";
01158 
01159                 surf = scale_surface(image_, w, h, false);
01160             }
01161         }
01162         src_clip.w = w;
01163         src_clip.h = h;
01164     } else {
01165         surf = image_;
01166     }
01167 
01168     if(vertical_mirror_(local_variables)) {
01169         surf = flip_surface(surf, false);
01170     }
01171 
01172     blit_surface(surf, &src_clip, canvas, &dst_clip);
01173 }
01174 
01175 timage::tresize_mode timage::get_resize_mode(const std::string& resize_mode)
01176 {
01177     if(resize_mode == "tile") {
01178         return timage::tile;
01179     } else if(resize_mode == "stretch") {
01180         return timage::stretch;
01181     } else {
01182         if(!resize_mode.empty() && resize_mode != "scale") {
01183             ERR_GUI_E << "Invalid resize mode '"
01184                     << resize_mode << "' falling back to 'scale'.\n";
01185         }
01186         return timage::scale;
01187     }
01188 }
01189 
01190 /***** ***** ***** ***** ***** TEXT ***** ***** ***** ***** *****/
01191 
01192 /** Definition of a text shape. */
01193 class ttext
01194     : public tcanvas::tshape
01195 {
01196 public:
01197 
01198     /**
01199      * Constructor.
01200      *
01201      * @param cfg                 The config object to define the text see
01202      *                            http://www.wesnoth.org/wiki/GUICanvasWML#Text
01203      *                            for more information.
01204      */
01205     explicit ttext(const config& cfg);
01206 
01207     /** Implement shape::draw(). */
01208     void draw(surface& canvas
01209             , const game_logic::map_formula_callable& variables);
01210 
01211 private:
01212     tformula<unsigned>
01213         x_, /**< The x coordinate of the text. */
01214         y_, /**< The y coordinate of the text. */
01215         w_, /**< The width of the text. */
01216         h_; /**< The height of the text. */
01217 
01218     /** The font size of the text. */
01219     unsigned font_size_;
01220 
01221     /** The style of the text. */
01222     unsigned font_style_;
01223 
01224     /** The alignment of the text. */
01225     tformula<PangoAlignment> text_alignment_;
01226 
01227     /** The color of the text. */
01228     Uint32 color_;
01229 
01230     /** The text to draw. */
01231     tformula<t_string> text_;
01232 
01233     /** The text markup switch of the text. */
01234     tformula<bool> text_markup_;
01235 
01236     /** The maximum width for the text. */
01237     tformula<int> maximum_width_;
01238 
01239     /** The number of characters per line. */
01240     unsigned characters_per_line_;
01241 
01242     /** The maximum height for the text. */
01243     tformula<int> maximum_height_;
01244 };
01245 
01246 ttext::ttext(const config& cfg)
01247     : x_(cfg["x"])
01248     , y_(cfg["y"])
01249     , w_(cfg["w"])
01250     , h_(cfg["h"])
01251     , font_size_(cfg["font_size"])
01252     , font_style_(decode_font_style(cfg["font_style"]))
01253     , text_alignment_(cfg["text_alignment"])
01254     , color_(decode_color(cfg["color"]))
01255     , text_(cfg["text"])
01256     , text_markup_(cfg["text_markup"], false)
01257     , maximum_width_(cfg["maximum_width"], -1)
01258     , characters_per_line_(cfg["text_characters_per_line"])
01259     , maximum_height_(cfg["maximum_height"], -1)
01260 {
01261 
01262 /*WIKI
01263  * @page = GUICanvasWML
01264  *
01265  * == Text ==
01266  * @begin{tag}{name="text"}{min="0"}{max="-1"}
01267  * Definition of text.
01268  *
01269  * Keys:
01270  * @begin{table}{config}
01271  *     x & f_unsigned & 0 &            The x coordinate of the top left corner. $
01272  *     y & f_unsigned & 0 &            The y coordinate of the top left corner. $
01273  *     w & f_unsigned & 0 &            The width of the text's bounding
01274  *                                     rectangle. $
01275  *     h & f_unsigned & 0 &            The height of the text's bounding
01276  *                                     rectangle. $
01277  *     font_size & unsigned & &        The size of the text font. $
01278  *     font_style & font_style & "" &  The style of the text. $
01279  *     text_alignment & f_h_align & "left" &
01280  *                                     The alignment of the text. $
01281  *     color & color & "" &            The color of the text. $
01282  *     text & f_tstring & "" &         The text to draw (translatable). $
01283  *     text_markup & f_bool & false &  Can the text have mark-up? $
01284  *     maximum_width & f_int & -1 &    The maximum width the text is allowed to
01285  *                                     be. $
01286  *     maximum_height & f_int & -1 &   The maximum height the text is allowed
01287  *                                     to be. $
01288  *     debug & string & "" &           Debug message to show upon creation
01289  *                                     this message is not stored. $
01290  * @end{table}
01291  * @end{tag}{name="text"}
01292  * NOTE alignment could only be done with the formulas, but now with the
01293  * text_alignment flag as well, older widgets might still use the formulas and
01294  * not all widgets may expose the text alignment yet and when exposed not use
01295  * it yet.
01296  *
01297  * Variables:
01298  * @begin{table}{formula}
01299  *     text_width & unsigned &            The width of the rendered text. $
01300  *     text_height & unsigned &           The height of the rendered text. $
01301  * @end{table}
01302  * Also the general variables are available, see [[#general_variables|Line]].
01303  * @end{parent}{name="generic/state/draw/"}
01304  */
01305 
01306     VALIDATE(font_size_, _("Text has a font size of 0."));
01307 
01308     const std::string& debug = (cfg["debug"]);
01309     if(!debug.empty()) {
01310         DBG_GUI_P << "Text: found debug message '" << debug << "'.\n";
01311     }
01312 }
01313 
01314 void ttext::draw(surface& canvas
01315         , const game_logic::map_formula_callable& variables)
01316 {
01317     assert(variables.has_key("text"));
01318 
01319     // We first need to determine the size of the text which need the rendered
01320     // text. So resolve and render the text first and then start to resolve
01321     // the other formulas.
01322     const t_string text = text_(variables);
01323 
01324     if(text.empty()) {
01325         DBG_GUI_D << "Text: no text to render, leave.\n";
01326         return;
01327     }
01328 
01329     static font::ttext text_renderer;
01330     text_renderer.set_text(text, text_markup_(variables));
01331 
01332     text_renderer.set_font_size(font_size_)
01333             .set_font_style(font_style_)
01334             .set_alignment(text_alignment_(variables))
01335             .set_foreground_color(color_)
01336             .set_maximum_width(maximum_width_(variables))
01337             .set_maximum_height(maximum_height_(variables))
01338             .set_ellipse_mode(variables.has_key("text_wrap_mode")
01339                 ? static_cast<PangoEllipsizeMode>
01340                     (variables.query_value("text_wrap_mode").as_int())
01341                 : PANGO_ELLIPSIZE_END)
01342             .set_characters_per_line(characters_per_line_);
01343 
01344     surface surf = text_renderer.render();
01345     if(surf->w == 0) {
01346         DBG_GUI_D  << "Text: Rendering '"
01347                 << text << "' resulted in an empty canvas, leave.\n";
01348         return;
01349     }
01350 
01351     game_logic::map_formula_callable local_variables(variables);
01352     local_variables.add("text_width", variant(surf->w));
01353     local_variables.add("text_height", variant(surf->h));
01354 /*
01355     std::cerr << "Text: drawing text '" << text
01356         << " maximum width " << maximum_width_(variables)
01357         << " maximum height " << maximum_height_(variables)
01358         << " text width " << surf->w
01359         << " text height " << surf->h;
01360 */
01361     ///@todo formulas are now recalculated every draw cycle which is a
01362     // bit silly unless there has been a resize. So to optimize we should
01363     // use an extra flag or do the calculation in a separate routine.
01364 
01365     const unsigned x = x_(local_variables);
01366     const unsigned y = y_(local_variables);
01367     const unsigned w = w_(local_variables);
01368     const unsigned h = h_(local_variables);
01369 
01370     DBG_GUI_D << "Text: drawing text '" << text
01371             << "' drawn from " << x << ',' << y
01372             << " width " << w << " height " << h
01373             << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
01374 
01375     VALIDATE(static_cast<int>(x) < canvas->w && static_cast<int>(y) < canvas->h
01376             , _("Text doesn't start on canvas."));
01377 
01378     // A text might be to long and will be clipped.
01379     if(surf->w > static_cast<int>(w)) {
01380         WRN_GUI_D << "Text: text is too wide for the "
01381                 "canvas and will be clipped.\n";
01382     }
01383 
01384     if(surf->h > static_cast<int>(h)) {
01385         WRN_GUI_D << "Text: text is too high for the "
01386                 "canvas and will be clipped.\n";
01387     }
01388 
01389     SDL_Rect dst = ::create_rect(x, y, canvas->w, canvas->h);
01390     blit_surface(surf, 0, canvas, &dst);
01391 }
01392 
01393 } // namespace
01394 
01395 /***** ***** ***** ***** ***** CANVAS ***** ***** ***** ***** *****/
01396 
01397 tcanvas::tcanvas()
01398     : shapes_()
01399     , blur_depth_(0)
01400     , w_(0)
01401     , h_(0)
01402     , canvas_()
01403     , variables_()
01404     , dirty_(true)
01405 {
01406 }
01407 
01408 void tcanvas::draw(const bool force)
01409 {
01410     log_scope2(log_gui_draw, "Canvas: drawing.");
01411     if(!dirty_ && !force) {
01412         DBG_GUI_D << "Canvas: nothing to draw.\n";
01413         return;
01414     }
01415 
01416     if(dirty_) {
01417         get_screen_size_variables(variables_);
01418         variables_.add("width",variant(w_));
01419         variables_.add("height",variant(h_));
01420     }
01421 
01422     // create surface
01423     DBG_GUI_D << "Canvas: create new empty canvas.\n";
01424     canvas_.assign(create_neutral_surface(w_, h_));
01425 
01426     // draw items
01427     for(std::vector<tshape_ptr>::iterator itor =
01428             shapes_.begin(); itor != shapes_.end(); ++itor) {
01429         log_scope2(log_gui_draw, "Canvas: draw shape.");
01430 
01431         (*itor)->draw(canvas_, variables_);
01432     }
01433 
01434     dirty_ = false;
01435 }
01436 
01437 void tcanvas::blit(surface& surf, SDL_Rect rect)
01438 {
01439     draw();
01440 
01441     if(blur_depth_) {
01442         if(is_neutral(surf)) {
01443             blur_surface(surf, rect, blur_depth_);
01444         } else {
01445             // Can't directly blur the surface if not 32 bpp.
01446             SDL_Rect r = rect;
01447             ///@todo we should use: get_surface_portion(surf, r, false)
01448             ///no need to optimize format, since blur_surface will undo it
01449             surface s = get_surface_portion(surf, r, true);
01450             s = blur_surface(s, blur_depth_);
01451             sdl_blit(s, NULL, surf, &rect);
01452         }
01453     }
01454 
01455     sdl_blit(canvas_, NULL, surf, &rect);
01456 }
01457 
01458 void tcanvas::parse_cfg(const config& cfg)
01459 {
01460     log_scope2(log_gui_parse, "Canvas: parsing config.");
01461     shapes_.clear();
01462 
01463     foreach(const config::any_child& shape, cfg.all_children_range()) {
01464         const std::string &type = shape.key;
01465         const config &data = shape.cfg;
01466 
01467         DBG_GUI_P << "Canvas: found shape of the type " << type << ".\n";
01468 
01469         if(type == "line") {
01470             shapes_.push_back(new tline(data));
01471         } else if(type == "rectangle") {
01472             shapes_.push_back(new trectangle(data));
01473         } else if(type == "circle") {
01474             shapes_.push_back(new tcircle(data));
01475         } else if(type == "image") {
01476             shapes_.push_back(new timage(data));
01477         } else if(type == "text") {
01478             shapes_.push_back(new ttext(data));
01479         } else if(type == "pre_commit") {
01480 
01481             /* note this should get split if more preprocessing is used. */
01482             foreach(const config::any_child& function,
01483                     data.all_children_range()) {
01484 
01485                 if(function.key == "blur") {
01486                     blur_depth_ = function.cfg["depth"];
01487                 } else {
01488                     ERR_GUI_P << "Canvas: found a pre commit function"
01489                             << " of an invalid type " << type << ".\n";
01490                 }
01491             }
01492 
01493         } else {
01494             ERR_GUI_P << "Canvas: found a shape of an invalid type "
01495                     << type << ".\n";
01496 
01497             assert(false);
01498         }
01499     }
01500 }
01501 
01502 /***** ***** ***** ***** ***** SHAPE ***** ***** ***** ***** *****/
01503 
01504 } // namespace gui2
01505 /*WIKI
01506  * @page = GUICanvasWML
01507  * @order = ZZZZZZ_footer
01508  *
01509  * [[Category: WML Reference]]
01510  * [[Category: GUI WML Reference]]
01511  *
01512  */
01513 
01514 /*WIKI
01515  * @page = GUIVariable
01516  * @order = ZZZZZZ_footer
01517  *
01518  * [[Category: WML Reference]]
01519  * [[Category: GUI WML Reference]]
01520  */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:40 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs