canvas.cpp

Go to the documentation of this file.
00001 /* $Id: canvas.cpp 26650 2008-05-16 15:55:56Z mordante $ */
00002 /*
00003    Copyright (C) 2007 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file canvas.cpp
00016 //! Implementation of canvas.hpp.
00017 
00018 #include "gui/widgets/canvas.hpp"
00019 
00020 #include "config.hpp"
00021 #include "font.hpp"
00022 #include "image.hpp"
00023 #include "gettext.hpp"
00024 #include "gui/widgets/formula.hpp"
00025 #include "gui/widgets/helper.hpp"
00026 #include "log.hpp"
00027 #include "sdl_utils.hpp"
00028 #include "serialization/parser.hpp"
00029 #include "wml_exception.hpp"
00030 
00031 #include <algorithm>
00032 #include <cassert>
00033 
00034 #define DBG_G LOG_STREAM_INDENT(debug, gui)
00035 #define LOG_G LOG_STREAM_INDENT(info, gui)
00036 #define WRN_G LOG_STREAM_INDENT(warn, gui)
00037 #define ERR_G LOG_STREAM_INDENT(err, gui)
00038 
00039 #define DBG_G_D LOG_STREAM_INDENT(debug, gui_draw)
00040 #define LOG_G_D LOG_STREAM_INDENT(info, gui_draw)
00041 #define WRN_G_D LOG_STREAM_INDENT(warn, gui_draw)
00042 #define ERR_G_D LOG_STREAM_INDENT(err, gui_draw)
00043 
00044 #define DBG_G_E LOG_STREAM_INDENT(debug, gui_event)
00045 #define LOG_G_E LOG_STREAM_INDENT(info, gui_event)
00046 #define WRN_G_E LOG_STREAM_INDENT(warn, gui_event)
00047 #define ERR_G_E LOG_STREAM_INDENT(err, gui_event)
00048 
00049 #define DBG_G_P LOG_STREAM_INDENT(debug, gui_parse)
00050 #define LOG_G_P LOG_STREAM_INDENT(info, gui_parse)
00051 #define WRN_G_P LOG_STREAM_INDENT(warn, gui_parse)
00052 #define ERR_G_P LOG_STREAM_INDENT(err, gui_parse)
00053 
00054 namespace gui2{
00055 
00056 //! Definition of a line shape.
00057 class tline : public tcanvas::tshape
00058 {
00059 public:
00060     tline(const config& cfg);
00061 
00062     //! Implement shape::draw().
00063     void draw(surface& canvas,
00064         const game_logic::map_formula_callable& variables);
00065 
00066 private:
00067     tformula<unsigned> 
00068         x1_, 
00069         y1_,
00070         x2_,
00071         y2_;
00072 
00073     Uint32 colour_;
00074     //! The thickness of the line:
00075     //! if the value is odd the x and y are the middle of the line.
00076     //! if the value is even the x and y are the middle of a line
00077     //! with width - 1. (0 is special case, does nothing.)
00078     unsigned thickness_;
00079 };
00080 
00081 tline::tline(const config& cfg) :
00082     x1_(cfg["x1"]),
00083     y1_(cfg["y1"]),
00084     x2_(cfg["x2"]),
00085     y2_(cfg["y2"]),
00086     colour_(decode_colour(cfg["colour"])),
00087     thickness_(lexical_cast_default<unsigned>(cfg["thickness"]))
00088 {
00089 /*WIKI
00090  * @page = GUICanvasWML
00091  *
00092  * == Line ==
00093  * Definition of a line. When drawing a line it doesn't get blended on the
00094  * surface but replaces the pixels instead. A blitting flag might be added later
00095  * if needed.
00096  *
00097  * Keys: 
00098  * @start_table = config
00099  *     x1 (f_unsigned = 0)             The x coordinate of the startpoint.
00100  *     y1 (f_unsigned = 0)             The y coordinate of the startpoint.
00101  *     x2 (f_unsigned = 0)             The x coordinate of the endpoint.
00102  *     y2 (f_unsigned = 0)             The y coordinate of the endpoint.
00103  *     colour (widget = "")            The colour of the line.
00104  *     thickness = (unsigned = 0)      The thickness of the line if 0 nothing
00105  *                                     is drawn.
00106  *     debug = (string = "")           Debug message to show upon creation
00107  *                                     this message is not stored.
00108  * @end_table                                    
00109  *
00110  * <span id="general_variables">Variables:</span>.
00111  * @start_table = formula
00112  *     width unsigned                  The width of the canvas.
00113  *     height unsigned                 The height of the canvas.
00114  *     text tstring                    The text to render on the widget.
00115  *@end_table
00116  *
00117  * Note when drawing the valid coordinates are:<br>
00118  * 0 -> width - 1 <br>
00119  * 0 -> height -1 
00120  *
00121  * Drawing outside this area will result in unpredicatable results including
00122  * crashing. (That should be fixed, when encountered.)
00123  *
00124  */
00125 
00126 /*WIKI
00127  * This code can be used by a parser to generate the wiki page
00128  * structure
00129  * [tag name]
00130  * param type_info description
00131  *
00132  * param                               Name of the parameter.
00133  * 
00134  * type_info = ( type = default_value) The info about a optional parameter.
00135  * type_info = ( type )                The info about a mandatory parameter
00136  * type_info = [ type_info ]           The info about a conditional parameter 
00137  *                                     description should explain the reason.
00138  *
00139  * description                         Description of the parameter.
00140  *
00141  *
00142  *
00143  *
00144  * Formulas are a funtion between brackets, that way the engine can see whether
00145  * there is standing a plain number or a formula eg:
00146  * 0     A value of zero
00147  * (0)   A formula returning zero
00148  *
00149  * When formulas are available the text should state the available variables
00150  * which are available in that function.
00151  */
00152 
00153 /*WIKI
00154  * @page = GUIVariable
00155  *
00156  * = Variables =
00157  *
00158  * In various parts of the GUI engine the following table contains all valid 
00159  * variable types. Note this page is automatically generated.
00160  *
00161  * @start_table = variable_types
00162  *     unsigned                        Unsigned number (positive whole numbers
00163  *                                     and zero).
00164  *     f_unsigned                      Unsinged number or formula returning an
00165  *                                     unsigned number.
00166  *     int                             Signed number (whole numbers).
00167  *     f_int                           Signed number or formula returning an
00168  *                                     signed number.
00169  *     bool                            A boolean value accepts the normal 
00170  *                                     values as the rest of the game.
00171  *     string                          A text.
00172  *     tstring                         A translatable string.
00173  *     f_tstring                       Formula returning a translatable string.
00174  *
00175  *     colour                          A string which constains the colour, this
00176  *                                     a group of 4 numbers between 0 and 255
00177  *                                     separated by a comma. The numbers are red
00178  *                                     component, green component, blue 
00179  *                                     component and alpha. A colour of 0 is not
00180  *                                     available. An alpha of 255 is fully 
00181  *                                     transparent. Ommitted values are set to 0.
00182  *
00183  *     font_style                      A string which contains the style of the
00184  *                                     font:
00185  *                                     @* normal    normal font
00186  *                                     @* bold      bold font
00187  *                                     @* italic    italic font
00188  *                                     @* underline underlined font
00189  *                                     Since SDL has problems combining these
00190  *                                     styles only one can be picked. Once SDL
00191  *                                     will allow multiple options, this type
00192  *                                     will be transformed to a comma separated
00193  *                                     list. If empty we default to the normal
00194  *                                     style.
00195  *
00196  *     section                         A section is a generic variable for a
00197  *                                     WML section. The description should
00198  *                                     explain the exact type.
00199  *
00200  *     v_align                         Vertical alignment; how an item is
00201  *                                     aligned vertically in the available
00202  *                                     space. Possible values:
00203  *                                     @* top    aligned at the top  
00204  *                                     @* bottom aligned at the bottom
00205  *                                     @* center centered 
00206  *                                     When nothing is set or an another value
00207  *                                     as in the list the item is centred.
00208  *
00209  *     h_align                         Horizontal alignment; how an item is
00210  *                                     aligned horizontal in the available
00211  *                                     space. Possible values:
00212  *                                     @* top    aligned at the top  
00213  *                                     @* bottom aligned at the bottom
00214  *                                     @* center centered 
00215  *
00216  *     border                          Comma separated list of borders to use.
00217  *                                     Possible values:
00218  *                                     @* left   border at the left side
00219  *                                     @* right  border at the right side
00220  *                                     @* top    border at the top
00221  *                                     @* bottom border at the bottom
00222  *                                     @* all    alias for "left, right, top,
00223  *                                     bottom"
00224  *
00225  * @end_table
00226  */
00227 
00228     const std::string& debug = (cfg["debug"]);
00229     if(!debug.empty()) {
00230         DBG_G_P << "Line: found debug message '" << debug << "'.\n";
00231     }
00232 
00233 }
00234 
00235 void tline::draw(surface& canvas,
00236     const game_logic::map_formula_callable& variables)
00237 {
00238     //@todo formulas are now recalculated every draw cycle which is a 
00239     // bit silly unless there has been a resize. So to optimize we should
00240     // use an extra flag or do the calculation in a separate routine.
00241     
00242     const unsigned x1 = x1_(variables);
00243     const unsigned y1 = y1_(variables);
00244     const unsigned x2 = x2_(variables);
00245     const unsigned y2 = y2_(variables);
00246 
00247     DBG_G_D << "Line: draw from " 
00248         << x1 << ',' << y1 << " to " << x2 << ',' << y2 
00249         << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00250 
00251     VALIDATE(x1 < canvas->w && x2 < canvas->w && y1 < canvas->h 
00252         && y2 < canvas->h, _("Line doesn't fit on canvas."));
00253 
00254     // FIXME respect the thickness.
00255 
00256     // now draw the line we use Bresenham's algorithm, which doesn't
00257     // support antialiasing. The advantage is that it's easy for testing.
00258     
00259     // lock the surface
00260     surface_lock locker(canvas);
00261     if(x1 > x2) {
00262         // invert points
00263         draw_line(canvas, colour_, x2, y2, x1, y1);
00264     } else {
00265         draw_line(canvas, colour_, x1, y1, x2, y2);
00266     }
00267 }
00268 
00269 
00270 
00271 //! Definition of a rectangle shape.
00272 class trectangle : public tcanvas::tshape
00273 {
00274 public:
00275     trectangle(const config& cfg);
00276 
00277     //! Implement shape::draw().
00278     void draw(surface& canvas,
00279         const game_logic::map_formula_callable& variables);
00280 
00281 private:
00282     tformula<unsigned> 
00283         x_, 
00284         y_,
00285         w_,
00286         h_;
00287 
00288     //! Border thickness if 0 the fill colour is used for the entire 
00289     //! widget.
00290     unsigned border_thickness_;
00291     Uint32 border_colour_;
00292 
00293     Uint32 fill_colour_;
00294 };
00295 
00296 trectangle::trectangle(const config& cfg) :
00297     x_(cfg["x"]),
00298     y_(cfg["y"]),
00299     w_(cfg["w"]),
00300     h_(cfg["h"]),
00301     border_thickness_(lexical_cast_default<unsigned>(cfg["border_thickness"])),
00302     border_colour_(decode_colour(cfg["border_colour"])),
00303     fill_colour_(decode_colour(cfg["fill_colour"]))
00304 {
00305 /*WIKI
00306  * @page = GUICanvasWML
00307  *
00308  * == Rectangle ==
00309  *
00310  * Definition of a rectangle. When drawing a rectangle it doesn't get blended on
00311  * the surface but replaces the pixels instead. A blitting flag might be added
00312  * later if needed.
00313  *
00314  * Keys: 
00315  * @start_table = config
00316  *     x (f_unsigned = 0)              The x coordinate of the top left corner.
00317  *     y (f_unsigned = 0)              The y coordinate of the top left corner.
00318  *     w (f_unsigned = 0)              The width of the rectangle.
00319  *     h (f_unsigned = 0)              The height of the rectangle.
00320  *     border_thickness (unsigned = 0) The thickness of the border if the 
00321  *                                     thickness is zero it's not drawn.
00322  *     border_colour (colour = "")     The colour of the border if empty it's
00323  *                                     not drawn.
00324  *     fill_colour (colour = "")       The colour of the interior if ommitted
00325  *                                     it's not drawn.
00326  *     debug = (string = "")           Debug message to show upon creation
00327  *                                     this message is not stored.
00328  * @end_table
00329  * Variables:
00330  * See [[#general_variables|Line]].
00331  *
00332  */
00333     if(border_colour_ == 0) {
00334         border_thickness_ = 0;
00335     }
00336 
00337     const std::string& debug = (cfg["debug"]);
00338     if(!debug.empty()) {
00339         DBG_G_P << "Rectangle: found debug message '" << debug << "'.\n";
00340     }
00341 }
00342 
00343 void trectangle::draw(surface& canvas,
00344     const game_logic::map_formula_callable& variables)
00345 {
00346 
00347     //@todo formulas are now recalculated every draw cycle which is a 
00348     // bit silly unless there has been a resize. So to optimize we should
00349     // use an extra flag or do the calculation in a separate routine.
00350     const unsigned x = x_(variables);
00351     const unsigned y = y_(variables);
00352     const unsigned w = w_(variables);
00353     const unsigned h = h_(variables);
00354 
00355     DBG_G_D << "Rectangle: draw from " << x << ',' << y
00356         << " width " << w << " height " << h 
00357         << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00358 
00359     VALIDATE(x < canvas->w && x + w <= canvas->w && y < canvas->h 
00360         && y + h <= canvas->h, _("Rectangle doesn't fit on canvas."));
00361 
00362 
00363     surface_lock locker(canvas);
00364 
00365     // draw the border
00366     for(unsigned i = 0; i < border_thickness_; ++i) {
00367 
00368         const unsigned left = x + i;
00369         const unsigned right = left + w - (i * 2) - 1;
00370         const unsigned top = y + i;
00371         const unsigned bottom = top + h - (i * 2) - 1;
00372 
00373         // top horizontal (left -> right)
00374         draw_line(canvas, border_colour_, left, top, right, top);
00375 
00376         // right vertical (top -> bottom)
00377         draw_line(canvas, border_colour_, right, top, right, bottom);
00378 
00379         // bottom horizontal (left -> right)
00380         draw_line(canvas, border_colour_, left, bottom, right, bottom);
00381 
00382         // left vertical (top -> bottom)
00383         draw_line(canvas, border_colour_, left, top, left, bottom);
00384 
00385     }
00386 
00387     // The fill_rect_alpha code below fails, can't remember the exact cause
00388     // so use the slow line drawing method to fill the rect.
00389     if(fill_colour_) {
00390         
00391         const unsigned left = x + border_thickness_;
00392         const unsigned right = left + w - (2 * border_thickness_) - 1;
00393         const unsigned top = y + border_thickness_;
00394         const unsigned bottom = top + h - (2 * border_thickness_);
00395 
00396         for(unsigned i = top; i < bottom; ++i) {
00397             
00398             draw_line(canvas, fill_colour_, left, i, right, i);
00399         }
00400     }
00401 /*
00402     const unsigned left = x_ + border_thickness_ + 1;
00403     const unsigned top = y_ + border_thickness_ + 1;
00404     const unsigned width = w_ - (2 * border_thickness_) - 2;
00405     const unsigned height = h_ - (2 * border_thickness_) - 2;
00406     SDL_Rect rect = create_rect(left, top, width, height);
00407 
00408     const Uint32 colour = fill_colour_ & 0xFFFFFF00;
00409     const Uint8 alpha = fill_colour_ & 0xFF;
00410 
00411     // fill
00412     fill_rect_alpha(rect, colour, alpha, canvas);
00413     canvas = blend_surface(canvas, 255, 0xAAAA00);  
00414 */  
00415 }
00416 
00417 
00418 
00419 //! Definition of an image shape.
00420 class timage : public tcanvas::tshape
00421 {
00422 public:
00423     timage(const config& cfg);
00424     
00425     //! Implement shape::draw().
00426     void draw(surface& canvas,
00427         const game_logic::map_formula_callable& variables);
00428 
00429 private:
00430     tformula<unsigned>
00431         x_, 
00432         y_,
00433         w_,
00434         h_;
00435 
00436     SDL_Rect src_clip_;
00437     SDL_Rect dst_clip_;
00438     surface image_;
00439 
00440     bool stretch_;
00441 };
00442 
00443 timage::timage(const config& cfg) :
00444     x_(cfg["x"]),
00445     y_(cfg["y"]),
00446     w_(cfg["w"]),
00447     h_(cfg["h"]),
00448     src_clip_(),
00449     dst_clip_(),
00450     image_(),
00451     stretch_(utils::string_bool(cfg["stretch"]))
00452 {
00453 /*WIKI
00454  * @page = GUICanvasWML
00455  *
00456  * == Image ==
00457  * Definition of an image.
00458  *
00459  * Keys: 
00460  * @start_table = config
00461  *     x (f_unsigned = 0)              The x coordinate of the top left corner.
00462  *     y (f_unsigned = 0)              The y coordinate of the top left corner.
00463  *     w (f_unsigned = 0)              The width of the image, if not zero the
00464  *                                     image will be scaled to the desired width.
00465  *     h (f_unsigned = 0)              The height of the image, if not zero the
00466  *                                     image will be scaled to the desired height.
00467  *     stretch (bool = false)          Border images often need to be either 
00468  *                                     stretched in the width or the height. If
00469  *                                     that's the case use stretch. It only works
00470  *                                     if only the heigth or the width is not zero.
00471  *                                     It will copy the first pixel the the others.
00472  *     name (string = "")              The name of the image.
00473  *     debug = (string = "")           Debug message to show upon creation
00474  *                                     this message is not stored.
00475  *
00476  * @end_table
00477  * Variables:
00478  * See [[#general_variables|Line]].
00479  *
00480  */
00481 
00482     image_.assign(make_neutral_surface(
00483         image::get_image(image::locator(cfg["name"]))));
00484     src_clip_ = ::create_rect(0, 0, image_->w, image_->h);
00485 
00486     const std::string& debug = (cfg["debug"]);
00487     if(!debug.empty()) {
00488         DBG_G_P << "Image: found debug message '" << debug << "'.\n";
00489     }
00490 }
00491 
00492 void timage::draw(surface& canvas,
00493     const game_logic::map_formula_callable& variables)
00494 {
00495     DBG_G_D << "Image: draw.\n";
00496 
00497     //@todo formulas are now recalculated every draw cycle which is a 
00498     // bit silly unless there has been a resize. So to optimize we should
00499     // use an extra flag or do the calculation in a separate routine.
00500     const unsigned x = x_(variables);
00501     const unsigned y = y_(variables);
00502     unsigned w = w_(variables);
00503     unsigned h = h_(variables);
00504 
00505     // Copy the data to local variables to avoid overwriting the originals.
00506     SDL_Rect src_clip = src_clip_;
00507     SDL_Rect dst_clip = dst_clip_;
00508     dst_clip.x = x;
00509     dst_clip.y = y;
00510     surface surf;
00511 
00512     // Test whether we need to scale and do the scaling if needed.
00513     if(w || h) {
00514         bool done = false;
00515         bool stretch = stretch_ && (!!w ^ !!h);
00516         if(!w) {
00517             if(stretch) { 
00518                 DBG_G_D << "Image: vertical stretch from " << image_->w 
00519                     << ',' << image_->h << " to a height of " << h << ".\n";
00520 
00521                 surf = stretch_surface_vertical(image_, h, false);  
00522                 done = true;
00523             }
00524             w = image_->w;
00525         }
00526 
00527         if(!h) {
00528             if(stretch) { 
00529                 DBG_G_D << "Image: horizontal stretch from " << image_->w 
00530                     << ',' << image_->h << " to a width of " << w << ".\n";
00531 
00532                 surf = stretch_surface_horizontal(image_, w, false);    
00533                 done = true;
00534             }
00535             h = image_->h;
00536         }
00537 
00538         if(!done) {
00539 
00540             DBG_G_D << "Image: scaling from " << image_->w 
00541                 << ',' << image_->h << " to " << w << ',' << h << ".\n";
00542 
00543             surf = scale_surface(image_, w, h, false);
00544         }
00545         src_clip.w = w;
00546         src_clip.h = h;
00547     } else {
00548         surf = image_;
00549     }
00550 
00551     blit_surface(surf, &src_clip, canvas, &dst_clip);
00552 }
00553 
00554 //! Definition of a text shape.
00555 class ttext : public tcanvas::tshape
00556 {
00557 public:
00558     ttext(const config& cfg);
00559     
00560     //! Implement shape::draw().
00561     void draw(surface& canvas,
00562         const game_logic::map_formula_callable& variables);
00563 
00564 private:
00565     tformula<unsigned>
00566         x_, 
00567         y_,
00568         w_,
00569         h_;
00570 
00571     unsigned font_size_;
00572     int font_style_;
00573     Uint32 colour_;
00574 
00575     tformula<t_string> text_;
00576 };
00577 
00578 ttext::ttext(const config& cfg) :
00579     x_(cfg["x"]),
00580     y_(cfg["y"]),
00581     w_(cfg["w"]),
00582     h_(cfg["h"]),
00583     font_size_(lexical_cast_default<unsigned>(cfg["font_size"])),
00584     font_style_(decode_font_style(cfg["font_style"])),
00585     colour_(decode_colour(cfg["colour"])),
00586     text_(cfg["text"])
00587 {
00588 
00589 /*WIKI
00590  * @page = GUICanvasWML
00591  *
00592  * == Text ==
00593  * Definition of text.
00594  *
00595  * Keys: 
00596  * @start_table = config
00597  *     x (f_unsigned = 0)              The x coordinate of the top left corner.
00598  *     y (f_unsigned = 0)              The y coordinate of the top left corner.
00599  *     w (f_unsigned = 0)              The width of the rectangle.
00600  *     h (f_unsigned = 0)              The height of the rectangle.
00601  *     font_size (unsigned = 0)        The size of the font to draw in.
00602  *     font_style (font_style = "")    The style of the text.
00603  *     colour (colour = "")            The colour of the text.
00604  *     text (tstring = "")             The text to draw (translatable).
00605  *     debug = (string = "")           Debug message to show upon creation
00606  *                                     this message is not stored.
00607  * @end_table
00608  * NOTE alignment can be done with the forumulas.
00609  *
00610  * Variables:
00611  * @start_table = formula
00612  *     text_width unsigned             The width of the rendered text.
00613  *     text_height unsigned            The height of the renedered text.
00614  * @end_table
00615  * Also the general variables are available, see [[#general_variables|Line]].
00616  *
00617  */
00618 
00619     const std::string& debug = (cfg["debug"]);
00620     if(!debug.empty()) {
00621         DBG_G_P << "Text: found debug message '" << debug << "'.\n";
00622     }
00623 }
00624 
00625 void ttext::draw(surface& canvas,
00626     const game_logic::map_formula_callable& variables)
00627 {
00628 
00629     assert(variables.has_key("text"));
00630 
00631     // We first need to determine the size of the text which need the rendered 
00632     // text. So resolve and render the text first and then start to resolve
00633     // the other formulas.
00634     const t_string text = text_(variables);
00635 
00636     if(text.empty()) {
00637         DBG_G_D << "Text: no text to render, leave.\n";
00638         return;
00639     }
00640 
00641     SDL_Color col = { (colour_ >> 24), (colour_ >> 16), (colour_ >> 8), colour_ };
00642     surface surf(font::get_rendered_text(text, font_size_, col, font_style_));
00643     assert(surf);
00644 
00645     game_logic::map_formula_callable local_variables(variables);
00646     local_variables.add("text_width", variant(surf->w));
00647     local_variables.add("text_height", variant(surf->h));
00648 
00649 
00650     //@todo formulas are now recalculated every draw cycle which is a 
00651     // bit silly unless there has been a resize. So to optimize we should
00652     // use an extra flag or do the calculation in a separate routine.
00653     const unsigned x = x_(local_variables);
00654     const unsigned y = y_(local_variables);
00655     const unsigned w = w_(local_variables);
00656     const unsigned h = h_(local_variables);
00657 
00658     DBG_G_D << "Text: drawing text '" << text
00659         << "' drawn from " << x << ',' << y
00660         << " width " << w << " height " << h 
00661         << " canvas size " << canvas->w << ',' << canvas->h << ".\n";
00662 
00663     VALIDATE(x < canvas->w && y < canvas->h, _("Text doesn't start on canvas."));
00664 
00665     // A text might be to long and will be clipped.
00666     if(surf->w > w) {
00667         WRN_G_D << "Text: text is too wide for the canvas and will be clipped.\n";
00668     }
00669     
00670     if(surf->h > h) {
00671         WRN_G_D << "Text: text is too high for the canvas and will be clipped.\n";
00672     }
00673 
00674     SDL_Rect dst = { x, y, canvas->w, canvas->h };
00675     blit_surface(surf, 0, canvas, &dst);
00676 }
00677 
00678 tcanvas::tcanvas() :
00679     shapes_(),
00680     w_(0),
00681     h_(0),
00682     canvas_(),
00683     variables_(),
00684     dirty_(true)
00685 {
00686 }
00687 
00688 tcanvas::tcanvas(const config& cfg) :
00689     shapes_(),
00690     w_(0),
00691     h_(0),
00692     canvas_(),
00693     variables_(),
00694     dirty_(true)
00695 {
00696     parse_cfg(cfg);
00697 }
00698 
00699 void tcanvas::draw(const config& cfg)
00700 {
00701     parse_cfg(cfg);
00702     draw(true);
00703 }
00704 
00705 void tcanvas::draw(const bool force)
00706 {
00707     log_scope2(gui_draw, "Canvas: drawing.");
00708     if(!dirty_ && !force) {
00709         DBG_G_D << "Canvas: nothing to draw.\n";
00710         return;
00711     }
00712 
00713     if(dirty_) {
00714         variables_.add("width",variant(w_));
00715         variables_.add("height",variant(h_));
00716     }
00717 
00718     // create surface
00719     DBG_G_D << "Canvas: create new empty canvas.\n";
00720     canvas_.assign(SDL_CreateRGBSurface(SDL_SWSURFACE, w_, h_, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000));
00721 
00722     // draw items 
00723     for(std::vector<tshape_ptr>::iterator itor = 
00724             shapes_.begin(); itor != shapes_.end(); ++itor) {
00725         log_scope2(gui_draw, "Canvas: draw shape.");
00726         
00727         (*itor)->draw(canvas_, variables_);
00728     }
00729 
00730     dirty_ = false;
00731 }
00732 
00733 void tcanvas::parse_cfg(const config& cfg)
00734 {
00735     log_scope2(gui_parse, "Canvas: parsing config.");
00736     shapes_.clear();
00737 
00738     for(config::all_children_iterator itor = 
00739             cfg.ordered_begin(); itor != cfg.ordered_end(); ++itor) {
00740 
00741         const std::string& type = *((*itor).first);;
00742         const config& data = *((*itor).second);
00743 
00744         DBG_G_P << "Canvas: found shape of the type " << type << ".\n";
00745 
00746         if(type == "line") {
00747             shapes_.push_back(new tline(data));
00748         } else if(type == "rectangle") {
00749             shapes_.push_back(new trectangle(data));
00750         } else if(type == "image") {
00751             shapes_.push_back(new timage(data));
00752         } else if(type == "text") {
00753             shapes_.push_back(new ttext(data));
00754         } else {
00755             ERR_G_P << "Canvas: found a shape of an invalid type " << type << ".\n";
00756             assert(false); // FIXME remove in production code.
00757         }
00758     }
00759 }
00760 
00761 void tcanvas::tshape::put_pixel(ptrdiff_t start, Uint32 colour, unsigned w, unsigned x, unsigned y)
00762 {
00763     *reinterpret_cast<Uint32*>(start + (y * w * 4) + x * 4) = colour;
00764 }
00765 
00766 // the surface should be locked
00767 // the colour should be a const and the value send should already
00768 // be good for the wanted surface
00769 void tcanvas::tshape::draw_line(surface& canvas, Uint32 colour, 
00770         const unsigned x1, unsigned y1, const unsigned x2, unsigned y2)
00771 {
00772     colour = SDL_MapRGBA(canvas->format, 
00773         ((colour & 0xFF000000) >> 24),
00774         ((colour & 0x00FF0000) >> 16),
00775         ((colour & 0x0000FF00) >> 8),
00776         ((colour & 0x000000FF)));
00777 
00778     ptrdiff_t start = reinterpret_cast<ptrdiff_t>(canvas->pixels);
00779     unsigned w = canvas->w;
00780 
00781     DBG_G_D << "Shape: draw line from " 
00782         << x1 << ',' << y1 << " to " << x2 << ',' << y2
00783         << " canvas width " << w << " canvas height "
00784         << canvas->h << ".\n";
00785 
00786     assert(x1 < canvas->w);
00787     assert(x2 < canvas->w);
00788     assert(y1 < canvas->h);
00789     assert(y2 < canvas->h);
00790 
00791     // use a special case for vertical lines
00792     if(x1 == x2) {
00793         if(y2 < y1) {
00794             std::swap(y1, y2);  
00795         }
00796 
00797         for(unsigned y = y1; y <= y2; ++y) {
00798             put_pixel(start, colour, w, x1, y);
00799         }
00800         return;
00801     } 
00802 
00803     // use a special case for horizontal lines
00804     if(y1 == y2) {
00805         for(unsigned x  = x1; x <= x2; ++x) {
00806             put_pixel(start, colour, w, x, y1);
00807         }
00808         return;
00809     } 
00810 
00811     // draw based on Bresenham on wikipedia
00812     int dx = x2 - x1;
00813     int dy = y2 - y1;
00814     int slope = 1;
00815     if (dy < 0) {
00816         slope = -1;
00817         dy = -dy;
00818     }
00819 
00820     // Bresenham constants
00821     int incE = 2 * dy;
00822     int incNE = 2 * dy - 2 * dx;
00823     int d = 2 * dy - dx;
00824     int y = y1;
00825 
00826     // Blit
00827     for (unsigned x = x1; x <= x2; ++x) {
00828         put_pixel(start, colour, w, x, y);
00829         if (d <= 0) {
00830             d += incE;
00831         } else {
00832             d += incNE;
00833             y += slope;
00834         }
00835     }
00836 }
00837 
00838 
00839 } // namespace gui2

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs