00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
00057 class tline : public tcanvas::tshape
00058 {
00059 public:
00060 tline(const config& cfg);
00061
00062
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
00075
00076
00077
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
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
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
00239
00240
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
00255
00256
00257
00258
00259
00260 surface_lock locker(canvas);
00261 if(x1 > x2) {
00262
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
00272 class trectangle : public tcanvas::tshape
00273 {
00274 public:
00275 trectangle(const config& cfg);
00276
00277
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
00289
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
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
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
00348
00349
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
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
00374 draw_line(canvas, border_colour_, left, top, right, top);
00375
00376
00377 draw_line(canvas, border_colour_, right, top, right, bottom);
00378
00379
00380 draw_line(canvas, border_colour_, left, bottom, right, bottom);
00381
00382
00383 draw_line(canvas, border_colour_, left, top, left, bottom);
00384
00385 }
00386
00387
00388
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
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 }
00416
00417
00418
00419
00420 class timage : public tcanvas::tshape
00421 {
00422 public:
00423 timage(const config& cfg);
00424
00425
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
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
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
00498
00499
00500 const unsigned x = x_(variables);
00501 const unsigned y = y_(variables);
00502 unsigned w = w_(variables);
00503 unsigned h = h_(variables);
00504
00505
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
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
00555 class ttext : public tcanvas::tshape
00556 {
00557 public:
00558 ttext(const config& cfg);
00559
00560
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
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
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
00632
00633
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
00651
00652
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
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
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
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);
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
00767
00768
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
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
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
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
00821 int incE = 2 * dy;
00822 int incNE = 2 * dy - 2 * dx;
00823 int d = 2 * dy - dx;
00824 int y = y1;
00825
00826
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 }