The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
display.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Routines to set up the display, scroll and zoom the map.
18  */
19 
20 #include "arrow.hpp"
21 #include "cursor.hpp"
22 #include "display.hpp"
23 #include "fake_unit_manager.hpp"
24 #include "game_preferences.hpp"
25 #include "gettext.hpp"
26 #include "halo.hpp"
27 #include "language.hpp"
28 #include "log.hpp"
29 #include "marked-up_text.hpp"
30 #include "map/map.hpp"
31 #include "map/label.hpp"
32 #include "minimap.hpp"
33 #include "overlay.hpp"
34 #include "play_controller.hpp" //note: this can probably be refactored out
35 #include "reports.hpp"
36 #include "resources.hpp"
37 #include "synced_context.hpp"
38 #include "team.hpp"
39 #include "terrain/builder.hpp"
40 #include "text.hpp"
41 #include "time_of_day.hpp"
42 #include "tooltips.hpp"
43 #include "tod_manager.hpp"
44 #include "units/unit.hpp"
46 #include "units/drawer.hpp"
47 #include "whiteboard/manager.hpp"
48 #include "show_dialog.hpp"
50 
51 #include <SDL_image.h>
52 
53 #ifdef __SUNPRO_CC
54 // GCC doesn't have hypot in cmath so include it for Sun Studio
55 #include <math.h>
56 #endif
57 #include <cmath>
58 
59 // Includes for bug #17573
60 
61 static lg::log_domain log_display("display");
62 #define ERR_DP LOG_STREAM(err, log_display)
63 #define LOG_DP LOG_STREAM(info, log_display)
64 #define DBG_DP LOG_STREAM(debug, log_display)
65 
66 namespace {
67  const int DefaultZoom = game_config::tile_size;
68  const int SmallZoom = DefaultZoom / 2;
69 
70  const int MinZoom = 4;
71  const int MaxZoom = 288;
72  size_t sunset_delay = 0;
73 
74  bool benchmark = false;
75 
76  bool debug_foreground = false;
77 }
78 
79 int display::last_zoom_ = SmallZoom;
80 
82 {
83  const team& curr_team = dc_->teams()[playing_team()];
84  const team& prev_team = dc_->teams()[playing_team()-1 < dc_->teams().size() ? playing_team()-1 : dc_->teams().size()-1];
85  for (const game_display::overlay_map::value_type i : *overlays_) {
86  const overlay& ov = i.second;
87  if (!ov.team_name.empty() &&
88  ((ov.team_name.find(curr_team.team_name()) + 1) != 0) !=
89  ((ov.team_name.find(prev_team.team_name()) + 1) != 0))
90  {
91  invalidate(i.first);
92  }
93  }
94 }
95 
96 
97 void display::add_overlay(const map_location& loc, const std::string& img, const std::string& halo, const std::string& team_name, const std::string& item_id, bool visible_under_fog)
98 {
99  if (halo_man_) {
100  const halo::handle halo_handle = halo_man_->add(get_location_x(loc) + hex_size() / 2,
101  get_location_y(loc) + hex_size() / 2, halo, loc);
102 
103  const overlay item(img, halo, halo_handle, team_name, item_id, visible_under_fog);
104  overlays_->insert(overlay_map::value_type(loc,item));
105  }
106 }
107 
109 {
110  /* This code no longer needed because of RAII in halo::handles
111  if (halo_man_) {
112  typedef overlay_map::const_iterator Itor;
113  std::pair<Itor,Itor> itors = overlays_->equal_range(loc);
114  while(itors.first != itors.second) {
115  halo_man_->remove(itors.first->second.halo_handle);
116  ++itors.first;
117  }
118  }
119  */
120 
121  overlays_->erase(loc);
122 }
123 
124 void display::remove_single_overlay(const map_location& loc, const std::string& toDelete)
125 {
126  //Iterate through the values with key of loc
127  typedef overlay_map::iterator Itor;
128  overlay_map::iterator iteratorCopy;
129  std::pair<Itor,Itor> itors = overlays_->equal_range(loc);
130  while(itors.first != itors.second) {
131  //If image or halo of overlay struct matches toDelete, remove the overlay
132  if(itors.first->second.image == toDelete || itors.first->second.halo == toDelete || itors.first->second.id == toDelete) {
133  iteratorCopy = itors.first;
134  ++itors.first;
135  //Not needed because of RAII --> halo_man_->remove(iteratorCopy->second.halo_handle);
136  overlays_->erase(iteratorCopy);
137  }
138  else {
139  ++itors.first;
140  }
141  }
142 }
143 
144 
145 
146 
147 display::display(const display_context * dc, CVideo& video, boost::weak_ptr<wb::manager> wb, reports & reports_object, const config& theme_cfg, const config& level, bool auto_join) :
148  video2::draw_layering(auto_join),
149  dc_(dc),
150  halo_man_(new halo::manager(*this)),
151  wb_(wb),
152  exclusive_unit_draw_requests_(),
153  screen_(video),
154  currentTeam_(0),
155  dont_show_all_(false),
156  energy_bar_rects_(),
157  xpos_(0),
158  ypos_(0),
159  view_locked_(false),
160  theme_(theme_cfg, screen_area()),
161  zoom_(DefaultZoom),
162  fake_unit_man_(new fake_unit_manager(*this)),
163  builder_(new terrain_builder(level, &dc_->map(), theme_.border().tile_image)),
164  minimap_(nullptr),
165  minimap_location_(sdl::empty_rect),
166  redrawMinimap_(false),
167  redraw_background_(true),
168  invalidateAll_(true),
169  grid_(false),
170  diagnostic_label_(0),
171  panelsDrawn_(false),
172  turbo_speed_(2),
173  turbo_(false),
174  invalidateGameStatus_(true),
175  map_labels_(new map_labels(*this, 0)),
176  reports_object_(&reports_object),
177  scroll_event_("scrolled"),
178  complete_redraw_event_("completely_redrawn"),
179  nextDraw_(0),
180  reportRects_(),
181 #ifdef SDL_GPU
182  reportImages_(),
183 #else
184  reportSurfaces_(),
185 #endif
186  reports_(),
187  menu_buttons_(),
188  action_buttons_(),
189  sliders_(),
190  invalidated_(),
191  previous_invalidated_(),
192  mouseover_hex_overlay_(nullptr),
193  tod_hex_mask1(nullptr),
194  tod_hex_mask2(nullptr),
195  fog_images_(),
196  shroud_images_(),
197  selectedHex_(),
198  mouseoverHex_(),
199  keys_(),
200  animate_map_(true),
201  animate_water_(true),
202  flags_(),
203  activeTeam_(0),
204  drawing_buffer_(),
205  map_screenshot_(false),
206  reach_map_(),
207  reach_map_old_(),
208  reach_map_changed_(true),
209  overlays_(nullptr),
210  fps_handle_(0),
211  invalidated_hexes_(0),
212  drawn_hexes_(0),
213  idle_anim_(preferences::idle_anim()),
214  idle_anim_rate_(1.0),
215  map_screenshot_surf_(nullptr),
216  redraw_observers_(),
217  draw_coordinates_(false),
218  draw_terrain_codes_(false),
219  arrows_map_(),
220  color_adjust_(),
221  dirty_()
222 #ifdef SDL_GPU
223  , update_panel_image_(true),
224  panel_image_()
225 #endif
226 {
227  //The following assertion fails when starting a campaign
228  assert(singleton_ == nullptr);
229  singleton_ = this;
230 
232 
233  blindfold_ctr_ = 0;
234 
235  read(level.child_or_empty("display"));
236 
237  if(video.non_interactive()
238  && (get_video_surface() != nullptr
239  && video.faked())) {
240  screen_.lock_updates(true);
241  }
242 
245 
247 
249 
250  init_flags();
251 
252  if(!menu_buttons_.empty() || !action_buttons_.empty() || !sliders_.empty() ) {
253  create_buttons();
254  }
255 
256 }
257 
259 {
260  singleton_ = nullptr;
261  resources::fake_units = nullptr;
262 }
263 
264 
266 
267  flags_.clear();
268  if (!dc_) return;
269  flags_.resize(dc_->teams().size());
270 
271  std::vector<std::string> side_colors;
272  side_colors.reserve(dc_->teams().size());
273 
274  for(size_t i = 0; i != dc_->teams().size(); ++i) {
275  std::string side_color = team::get_side_color_index(i+1);
276  side_colors.push_back(side_color);
277  init_flags_for_side_internal(i, side_color);
278  }
279  image::set_team_colors(&side_colors);
280 }
281 
283 {
284  if (!dc_ || side >= dc_->teams().size()) {
285  ERR_DP << "Cannot rebuild flags for inexistent or unconfigured side " << side << '\n';
286  return;
287  }
288 
290 }
291 
292 void display::init_flags_for_side_internal(size_t n, const std::string& side_color)
293 {
294  assert(dc_ != nullptr);
295  assert(n < dc_->teams().size());
296  assert(n < flags_.size());
297 
298  std::string flag = dc_->teams()[n].flag();
300  std::string new_rgb = side_color;
301 
302  if(flag.empty()) {
304  }
305 
306  LOG_DP << "Adding flag for team " << n << " from animation " << flag << "\n";
307 
308  // Must recolor flag image
309  animated<image::locator> temp_anim;
310 
311  std::vector<std::string> items = utils::square_parenthetical_split(flag);
312  std::vector<std::string>::const_iterator itor = items.begin();
313  for(; itor != items.end(); ++itor) {
314  const std::vector<std::string>& items = utils::split(*itor, ':');
315  std::string str;
316  int time;
317 
318  if(items.size() > 1) {
319  str = items.front();
320  time = std::stoi(items.back());
321  } else {
322  str = *itor;
323  time = 100;
324  }
325  std::stringstream temp;
326  temp << str << "~RC(" << old_rgb << ">"<< new_rgb << ")";
327  image::locator flag_image(temp.str());
328  temp_anim.add_frame(time, flag_image);
329  }
330 
332 
333  f = temp_anim;
334  f.start_animation(rand() % f.get_end_time(), true);
335 }
336 
337 #ifdef SDL_GPU
338 sdl::timage display::get_flag(const map_location& loc)
339 {
340  if(!get_map().is_village(loc)) {
341  return surface(nullptr);
342  }
343 
344  for(size_t i = 0; i != dc_->teams().size(); ++i) {
345  if(dc_->teams()[i].owns_village(loc) &&
346  (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(i+1)))
347  {
348  flags_[i].update_last_draw_time();
349  const image::locator &image_flag = animate_map_ ?
350  flags_[i].get_current_frame() : flags_[i].get_first_frame();
351  return image::get_texture(image_flag, image::TOD_COLORED);
352  }
353  }
354 
355  return sdl::timage();
356 }
357 #else
359 {
360  if(!get_map().is_village(loc)) {
361  return surface(nullptr);
362  }
363 
364  for(size_t i = 0; i != dc_->teams().size(); ++i) {
365  if(dc_->teams()[i].owns_village(loc) &&
366  (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(i+1)))
367  {
368  flags_[i].update_last_draw_time();
369  const image::locator &image_flag = animate_map_ ?
370  flags_[i].get_current_frame() : flags_[i].get_first_frame();
371  return image::get_image(image_flag, image::TOD_COLORED);
372  }
373  }
374 
375  return surface(nullptr);
376 }
377 #endif
378 
379 void display::set_team(size_t teamindex, bool show_everything)
380 {
381  assert(teamindex < dc_->teams().size());
382  currentTeam_ = teamindex;
383  if (!show_everything)
384  {
385  labels().set_team(&dc_->teams()[teamindex]);
386  dont_show_all_ = true;
387  }
388  else
389  {
390  labels().set_team(nullptr);
391  dont_show_all_ = false;
392  }
395  w->on_viewer_change(teamindex);
396 }
397 
398 void display::set_playing_team(size_t teamindex)
399 {
400  assert(teamindex < dc_->teams().size());
401  activeTeam_ = teamindex;
403 }
404 
406 {
407  if (loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end())
408  {
409  exclusive_unit_draw_requests_[loc] = unit.id();
410  return true;
411  }
412  else
413  {
414  return false;
415  }
416 }
417 
419 {
420  std::string id = "";
421  if(loc.valid())
422  {
424  //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
426  }
427  return id;
428 }
429 
430 const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
431 {
432  static time_of_day tod;
433  return tod;
434 }
435 
436 /**
437  * Display objects don't hold a tod maanger, instead game_display objects do. If the base version of this method is called,
438  * try to get it from resources and use an assert to check for failure.
439  */
441 {
442  assert(resources::tod_manager);
443  return *resources::tod_manager;
444 }
445 
447  const time_of_day& tod = get_time_of_day();
448  tod_color col = color_adjust_ + tod.color;
449  image::set_color_adjustment(col.r, col.g, col.b);
450 }
451 
452 void display::adjust_color_overlay(int r, int g, int b) {
453  color_adjust_ = tod_color(r, g, b);
454  update_tod();
455 }
456 
457 
458 void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
459 {
460  // search prefix.png, prefix1.png, prefix2.png ...
461  for(int i=0; ; ++i){
462  std::ostringstream s;
463  s << prefix;
464  if(i != 0)
465  s << i;
466  s << ".png";
467  if(image::exists(s.str()))
468  images.push_back(s.str());
469  else if(i>0)
470  break;
471  }
472  if (images.empty())
473  images.push_back("");
474 }
475 
476 const std::string& display::get_variant(const std::vector<std::string>& variants, const map_location &loc) const
477 {
478  //TODO use better noise function
479  return variants[abs(loc.x + loc.y) % variants.size()];
480 }
481 
483 {
484  builder_->rebuild_all();
485 }
486 
488 {
489  redraw_background_ = true;
490  builder_->reload_map();
491 }
492 
494 {
495  dc_ = dc;
496  builder_->change_map(&dc_->map()); //TODO: Should display_context own and initalize the builder object?
497 }
498 
500 {
501  halo_man_.reset(new halo::manager(*this));
502 }
503 
505 {
506  halo_man_.reset(&halo_man);
507 }
508 
510 {
511  if(value == true)
512  ++blindfold_ctr_;
513  else
514  --blindfold_ctr_;
515 }
516 
518 {
519  return blindfold_ctr_ > 0;
520 }
521 
522 
523 const SDL_Rect& display::max_map_area() const
524 {
525  static SDL_Rect max_area = {0, 0, 0, 0};
526 
527  // hex_size() is always a multiple of 4
528  // and hex_width() a multiple of 3,
529  // so there shouldn't be off-by-one-errors
530  // due to rounding.
531  // To display a hex fully on screen,
532  // a little bit extra space is needed.
533  // Also added the border two times.
534  max_area.w = static_cast<int>((get_map().w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
535  max_area.h = static_cast<int>((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size());
536 
537  return max_area;
538 }
539 
540 const SDL_Rect& display::map_area() const
541 {
542  static SDL_Rect max_area;
543  max_area = max_map_area();
544 
545  // if it's for map_screenshot, maximize and don't recenter
546  if (map_screenshot_) {
547  return max_area;
548  }
549 
550  static SDL_Rect res;
551  res = map_outside_area();
552 
553  if(max_area.w < res.w) {
554  // map is smaller, center
555  res.x += (res.w - max_area.w)/2;
556  res.w = max_area.w;
557  }
558 
559  if(max_area.h < res.h) {
560  // map is smaller, center
561  res.y += (res.h - max_area.h)/2;
562  res.h = max_area.h;
563  }
564 
565  return res;
566 }
567 
568 bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
569 {
570  const int x_thresh = hex_size();
571  const int y_thresh = hex_size();
572  return (x < area.x || x > area.x + area.w - x_thresh ||
573  y < area.y || y > area.y + area.h - y_thresh);
574 }
575 
576 // This function uses the screen as reference
577 const map_location display::hex_clicked_on(int xclick, int yclick) const
578 {
579  const SDL_Rect& rect = map_area();
580  if(sdl::point_in_rect(xclick,yclick,rect) == false) {
581  return map_location();
582  }
583 
584  xclick -= rect.x;
585  yclick -= rect.y;
586 
587  return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick);
588 }
589 
590 
591 // This function uses the rect of map_area as reference
593 {
594  // adjust for the border
595  x -= static_cast<int>(theme_.border().size * hex_width());
596  y -= static_cast<int>(theme_.border().size * hex_size());
597  // The editor can modify the border and this will result in a negative y
598  // value. Instead of adding extra cases we just shift the hex. Since the
599  // editor doesn't use the direction this is no problem.
600  const int offset = y < 0 ? 1 : 0;
601  if(offset) {
602  x += hex_width();
603  y += hex_size();
604  }
605  const int s = hex_size();
606  const int tesselation_x_size = hex_width() * 2;
607  const int tesselation_y_size = s;
608  const int x_base = x / tesselation_x_size * 2;
609  const int x_mod = x % tesselation_x_size;
610  const int y_base = y / tesselation_y_size;
611  const int y_mod = y % tesselation_y_size;
612 
613  int x_modifier = 0;
614  int y_modifier = 0;
615 
616  if (y_mod < tesselation_y_size / 2) {
617  if ((x_mod * 2 + y_mod) < (s / 2)) {
618  x_modifier = -1;
619  y_modifier = -1;
620  } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
621  x_modifier = 0;
622  y_modifier = 0;
623  } else {
624  x_modifier = 1;
625  y_modifier = -1;
626  }
627 
628  } else {
629  if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
630  x_modifier = -1;
631  y_modifier = 0;
632  } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
633  x_modifier = 0;
634  y_modifier = 0;
635  } else {
636  x_modifier = 1;
637  y_modifier = 0;
638  }
639  }
640 
641  return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
642 }
643 
645 {
646  if (loc_.y < rect_.bottom[loc_.x & 1])
647  ++loc_.y;
648  else {
649  ++loc_.x;
650  loc_.y = rect_.top[loc_.x & 1];
651  }
652 
653  return *this;
654 }
655 
656 // begin is top left, and end is after bottom right
658 {
659  return iterator(map_location(left, top[left & 1]), *this);
660 }
662 {
663  return iterator(map_location(right+1, top[(right+1) & 1]), *this);
664 }
665 
667 {
669 
670  if (r.w<=0 || r.h<=0) {
671  // empty rect, return dummy values giving begin=end
672  res.left = 0;
673  res.right = -1; // end is right+1
674  res.top[0] = 0;
675  res.top[1] = 0;
676  res.bottom[0] = 0;
677  res.bottom[1] = 0;
678  return res;
679  }
680 
681  SDL_Rect map_rect = map_area();
682  // translate rect coordinates from screen-based to map_area-based
683  int x = xpos_ - map_rect.x + r.x;
684  int y = ypos_ - map_rect.y + r.y;
685  // we use the "double" type to avoid important rounding error (size of an hex!)
686  // we will also need to use std::floor to avoid bad rounding at border (negative values)
687  double tile_width = hex_width();
688  double tile_size = hex_size();
689  double border = theme_.border().size;
690  // we minus "0.(3)", for horizontal imbrication.
691  // reason is: two adjacent hexes each overlap 1/4 of their width, so for
692  // grid calculation 3/4 of tile width is used, which by default gives
693  // 18/54=0.(3). Note that, while tile_width is zoom dependand, 0.(3) is not.
694  res.left = static_cast<int>(std::floor(-border + x / tile_width - 0.3333333));
695  // we remove 1 pixel of the rectangle dimensions
696  // (the rounded division take one pixel more than needed)
697  res.right = static_cast<int>(std::floor(-border + (x + r.w-1) / tile_width));
698 
699  // for odd x, we must shift up one half-hex. Since x will vary along the edge,
700  // we store here the y values for even and odd x, respectively
701  res.top[0] = static_cast<int>(std::floor(-border + y / tile_size));
702  res.top[1] = static_cast<int>(std::floor(-border + y / tile_size - 0.5));
703  res.bottom[0] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size));
704  res.bottom[1] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size - 0.5));
705 
706  // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
707  // (the 72x72 rectangle containing the hex) but not on the hex itself
708  // Can maybe be optimized by using pixel_position_to_hex
709 
710  return res;
711 }
712 
714 {
715  return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
716 }
717 
719 {
720  return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
721 }
722 
724 {
725  //TODO: don't return location for this,
726  // instead directly scroll to the clicked pixel position
727 
728  if (!sdl::point_in_rect(x, y, minimap_area())) {
729  return map_location();
730  }
731 
732  // we transform the coordinates from minimap to the full map image
733  // probably more adjustments to do (border, minimap shift...)
734  // but the mouse and human capacity to evaluate the rectangle center
735  // is not pixel precise.
736  int px = (x - minimap_location_.x) * get_map().w()*hex_width() / minimap_location_.w;
737  int py = (y - minimap_location_.y) * get_map().h()*hex_size() / minimap_location_.h;
738 
739  map_location loc = pixel_position_to_hex(px, py);
740  if (loc.x < 0)
741  loc.x = 0;
742  else if (loc.x >= get_map().w())
743  loc.x = get_map().w() - 1;
744 
745  if (loc.y < 0)
746  loc.y = 0;
747  else if (loc.y >= get_map().h())
748  loc.y = get_map().h() - 1;
749 
750  return loc;
751 }
752 
753 bool display::screenshot(const std::string& filename, bool map_screenshot)
754 {
755  bool res = false;
756 
757  if (!map_screenshot) {
758  surface& screenshot_surf = screen_.getSurface();
759 
760  res = image::save_image(screenshot_surf, filename);
761 #if 0
762  // FIXME: the SDL_SavePNG path results in oblique errors that don't
763  // make sense to anyone who's not familiarized with it, so
764  // we can't use this.
765  if (!res) {
766  ERR_DP << "Screenshot failed: " << SDL_GetError() << '\n';
767  }
768 #endif
769  } else {
770  if (get_map().empty()) {
771  ERR_DP << "No map loaded, cannot create a map screenshot.\n";
772  return false;
773  }
774 
775  SDL_Rect area = max_map_area();
777 
778  if (map_screenshot_surf_ == nullptr) {
779  // Memory problem ?
780  ERR_DP << "Could not create screenshot surface, try zooming out.\n";
781  return false;
782  }
783 
784  // back up the current map view position and move to top-left
785  int old_xpos = xpos_;
786  int old_ypos = ypos_;
787  xpos_ = 0;
788  ypos_ = 0;
789 
790  // we reroute render output to the screenshot surface and invalidate all
791  map_screenshot_= true ;
792  invalidateAll_ = true;
793  DBG_DP << "draw() with map_screenshot\n";
794  draw(true,true);
795 
796  // finally save the image on disk
797  res = image::save_image(map_screenshot_surf_,filename);
798 
799 #if 0
800  // FIXME: the SDL_SavePNG path results in oblique errors that don't
801  // make sense to anyone who's not familiarized with it, so
802  // we can't use this.
803  if (!res) {
804  // Need to do this ASAP or spurious messages result due to
805  // redraw_everything calling SDL too (e.g. "SDL_UpperBlit: passed
806  // a nullptr surface")
807  ERR_DP << "Map screenshot failed: " << SDL_GetError() << '\n';
808  }
809 #endif
810 
811  //NOTE: need to be sure that we free this huge surface (is it enough?)
812  map_screenshot_surf_ = nullptr;
813 
814  // restore normal rendering
815  map_screenshot_= false;
816  xpos_ = old_xpos;
817  ypos_ = old_ypos;
818  // some drawing functions are confused by the temporary change
819  // of the map_area and thus affect the UI outside of the map
821  }
822 
823  return res;
824 }
825 
826 std::shared_ptr<gui::button> display::find_action_button(const std::string& id)
827 {
828  for (size_t i = 0; i < action_buttons_.size(); ++i) {
829  if(action_buttons_[i]->id() == id) {
830  return action_buttons_[i];
831  }
832  }
833  return nullptr;
834 }
835 
836 std::shared_ptr<gui::button> display::find_menu_button(const std::string& id)
837 {
838  for (size_t i = 0; i < menu_buttons_.size(); ++i) {
839  if(menu_buttons_[i]->id() == id) {
840  return menu_buttons_[i];
841  }
842  }
843  return nullptr;
844 }
845 
846 std::shared_ptr<gui::zoom_slider> display::find_slider(const std::string& id)
847 {
848  for (size_t i = 0; i < sliders_.size(); ++i) {
849  if(sliders_[i]->id() == id) {
850  return sliders_[i];
851  }
852  }
853  return nullptr;
854 }
855 
857 {
858 
859  DBG_DP << "positioning sliders...\n";
860  const std::vector<theme::slider>& sliders = theme_.sliders();
861  for(std::vector<theme::slider>::const_iterator i = sliders.begin(); i != sliders.end(); ++i) {
862  std::shared_ptr<gui::zoom_slider> s = find_slider(i->get_id());
863  if (s) {
864  const SDL_Rect& loc = i->location(screen_area());
865  s->set_location(loc);
866  s->set_measurements(0,0);
867  }
868  }
869 
870  DBG_DP << "positioning menu buttons...\n";
871  const std::vector<theme::menu>& buttons = theme_.menus();
872  for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
873  std::shared_ptr<gui::button> b = find_menu_button(i->get_id());
874  if(b) {
875  const SDL_Rect& loc = i->location(screen_area());
876  b->set_location(loc);
877  b->set_measurements(0,0);
878  b->set_label(i->title());
879  b->set_image(i->image());
880  }
881  }
882 
883  DBG_DP << "positioning action buttons...\n";
884  const std::vector<theme::action>& actions = theme_.actions();
885  for(std::vector<theme::action>::const_iterator i = actions.begin(); i != actions.end(); ++i) {
886  std::shared_ptr<gui::button> b = find_action_button(i->get_id());
887  if(b) {
888  const SDL_Rect& loc = i->location(screen_area());
889  b->set_location(loc);
890  b->set_measurements(0,0);
891  b->set_label(i->title());
892  b->set_image(i->image());
893  }
894  }
895 }
896 
898 {
899  std::vector<std::shared_ptr<gui::button>> menu_work;
900  std::vector<std::shared_ptr<gui::button>> action_work;
901  std::vector<std::shared_ptr<gui::zoom_slider>> slider_work;
902 
903  DBG_DP << "creating sliders...\n";
904  const std::vector<theme::slider>& sliders = theme_.sliders();
905  for(std::vector<theme::slider>::const_iterator i = sliders.begin(); i != sliders.end(); ++i) {
906  std::shared_ptr<gui::zoom_slider> s(new gui::zoom_slider(screen_, i->image(), i->black_line()));
907  s->leave();
908  s->join_same(this);
909  DBG_DP << "drawing button " << i->get_id() << "\n";
910  s->set_id(i->get_id());
911  //TODO support for non zoom sliders
912  s->set_max(MaxZoom);
913  s->set_min(MinZoom);
914  s->set_value(zoom_);
915  if (!i->tooltip().empty()){
916  s->set_tooltip_string(i->tooltip());
917  }
918 
919  std::shared_ptr<gui::zoom_slider> s_prev = find_slider(s->id());
920  if(s_prev) {
921  //s_prev->leave();
922  s->set_max(s_prev->max_value());
923  s->set_min(s_prev->min_value());
924  s->set_value(s_prev->value());
925  s->enable(s_prev->enabled());
926  }
927 
928  slider_work.push_back(s);
929  }
930 
931  DBG_DP << "creating menu buttons...\n";
932  const std::vector<theme::menu>& buttons = theme_.menus();
933  for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
934 
935  if (!i->is_button()) continue;
936 
937  std::shared_ptr<gui::button> b(new gui::button(screen_, i->title(), gui::button::TYPE_PRESS, i->image(),
938  gui::button::DEFAULT_SPACE, false, i->overlay()));
939  DBG_DP << "drawing button " << i->get_id() << "\n";
940  b->join_same(this);
941  b->set_id(i->get_id());
942  if (!i->tooltip().empty()){
943  b->set_tooltip_string(i->tooltip());
944  }
945 
946  std::shared_ptr<gui::button> b_prev = find_menu_button(b->id());
947  if(b_prev) {
948  b->enable(b_prev->enabled());
949  }
950 
951  menu_work.push_back(b);
952  }
953  DBG_DP << "creating action buttons...\n";
954  const std::vector<theme::action>& actions = theme_.actions();
955  for(std::vector<theme::action>::const_iterator i = actions.begin(); i != actions.end(); ++i) {
956  std::shared_ptr<gui::button> b(new gui::button(screen_, i->title(), string_to_button_type(i->type()), i->image(),
957  gui::button::DEFAULT_SPACE, false, i->overlay()));
958 
959  DBG_DP << "drawing button " << i->get_id() << "\n";
960  b->set_id(i->get_id());
961  b->join_same(this);
962  if (!i->tooltip(0).empty()){
963  b->set_tooltip_string(i->tooltip(0));
964  }
965 
966  std::shared_ptr<gui::button> b_prev = find_action_button(b->id());
967  if(b_prev) b->enable(b_prev->enabled());
968 
969  action_work.push_back(b);
970  }
971 
972 
973  menu_buttons_.clear();
974  menu_buttons_.assign(menu_work.begin(), menu_work.end());
975  action_buttons_.clear();
976  action_buttons_.assign(action_work.begin(), action_work.end());
977  sliders_.clear();
978  sliders_.assign(slider_work.begin(), slider_work.end());
979 
980  layout_buttons();
981  DBG_DP << "buttons created\n";
982 }
983 
985 {
986  for (std::shared_ptr<gui::button> btn : menu_buttons_) {
987  btn->set_dirty(true);
988  }
989 
990  for (std::shared_ptr<gui::button> btn : action_buttons_) {
991  btn->set_dirty(true);
992  }
993 
994  for (std::shared_ptr<gui::slider> sld : sliders_) {
995  sld->set_dirty(true);
996  }
997 }
998 
999 
1001 {
1003  if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
1004  else if (type == "image") { res = gui::button::TYPE_IMAGE; }
1005  else if (type == "radiobox") { res = gui::button::TYPE_RADIO; }
1006  else if (type == "turbo") { res = gui::button::TYPE_TURBO; }
1007  return res;
1008 }
1009 
1010 static const std::string& get_direction(size_t n)
1011 {
1012  static std::string const dirs[6] = { "-n", "-ne", "-se", "-s", "-sw", "-nw" };
1013  return dirs[n >= sizeof(dirs)/sizeof(*dirs) ? 0 : n];
1014 }
1015 
1016 #ifdef SDL_GPU
1017 std::vector<sdl::timage> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
1018 #else
1019 std::vector<surface> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
1020 #endif
1021 {
1022  std::vector<std::string> names;
1023 
1024  map_location adjacent[6];
1025  get_adjacent_tiles(loc,adjacent);
1026 
1027  enum visibility {FOG=0, SHROUD=1, CLEAR=2};
1028  visibility tiles[6];
1029 
1030  const std::string* image_prefix[] =
1032 
1033  for(int i = 0; i != 6; ++i) {
1034  if(shrouded(adjacent[i])) {
1035  tiles[i] = SHROUD;
1036  } else if(!fogged(loc) && fogged(adjacent[i])) {
1037  tiles[i] = FOG;
1038  } else {
1039  tiles[i] = CLEAR;
1040  }
1041  }
1042 
1043  for(int v = FOG; v != CLEAR; ++v) {
1044  // Find somewhere that doesn't have overlap to use as a starting point
1045  int start;
1046  for(start = 0; start != 6; ++start) {
1047  if(tiles[start] != v) {
1048  break;
1049  }
1050  }
1051 
1052  if(start == 6) {
1053  // Completely surrounded by fog or shroud. This might have
1054  // a special graphic.
1055  const std::string name = *image_prefix[v] + "-all.png";
1056  if ( image::exists(name) ) {
1057  names.push_back(name);
1058  // Proceed to the next visibility (fog -> shroud -> clear).
1059  continue;
1060  }
1061  // No special graphic found. We'll just combine some other images
1062  // and hope it works out.
1063  start = 0;
1064  }
1065 
1066  // Find all the directions overlap occurs from
1067  for (int i = (start+1)%6, cap1 = 0; i != start && cap1 != 6; ++cap1) {
1068  if(tiles[i] == v) {
1069  std::ostringstream stream;
1070  std::string name;
1071  stream << *image_prefix[v];
1072 
1073  for (int cap2 = 0; v == tiles[i] && cap2 != 6; i = (i+1)%6, ++cap2) {
1074  stream << get_direction(i);
1075 
1076  if(!image::exists(stream.str() + ".png")) {
1077  // If we don't have any surface at all,
1078  // then move onto the next overlapped area
1079  if(name.empty()) {
1080  i = (i+1)%6;
1081  }
1082  break;
1083  } else {
1084  name = stream.str();
1085  }
1086  }
1087 
1088  if(!name.empty()) {
1089  names.push_back(name + ".png");
1090  }
1091  } else {
1092  i = (i+1)%6;
1093  }
1094  }
1095  }
1096 
1097  // now get the surfaces
1098 #ifdef SDL_GPU
1099  std::vector<sdl::timage> res;
1100 #else
1101  std::vector<surface> res;
1102 #endif
1103 
1104  for (std::string& name : names) {
1105 #ifdef SDL_GPU
1106  const sdl::timage img(image::get_texture(name, image_type));
1107  if (!img.null())
1108  res.push_back(img);
1109 #else
1110  const surface surf(image::get_image(name, image_type));
1111  if (surf)
1112  res.push_back(surf);
1113 #endif
1114  }
1115 
1116  return res;
1117 }
1118 
1119 #ifdef SDL_GPU
1120 std::vector<sdl::timage> display::get_terrain_images(
1121  const map_location &loc,
1122  const std::string& timeid,
1123  image::TYPE image_type,
1125 #else
1126 std::vector<surface> display::get_terrain_images(const map_location &loc,
1127  const std::string& timeid,
1128  image::TYPE image_type,
1129  TERRAIN_TYPE terrain_type)
1130 #endif
1131 {
1132 #ifdef SDL_GPU
1133  std::vector<sdl::timage> res;
1134 #else
1135  std::vector<surface> res;
1136 #endif
1137 
1138  terrain_builder::TERRAIN_TYPE builder_terrain_type =
1139  (terrain_type == FOREGROUND ?
1141 
1142  const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc,
1143  timeid, builder_terrain_type);
1144 
1146 
1147  const time_of_day& tod = get_time_of_day(loc);
1148 
1149  //get all the light transitions
1150  map_location adjs[6];
1151  get_adjacent_tiles(loc,adjs);
1152  for(int d=0; d<6; ++d){
1153  const time_of_day& atod = get_time_of_day(adjs[d]);
1154  if(atod.color == tod.color)
1155  continue;
1156 
1157  if(lt.empty()) {
1158  //color the full hex before adding transitions
1159  tod_color col = tod.color + color_adjust_;
1160  lt = image::get_light_string(6, col.r, col.g, col.b);
1161  }
1162 
1163  // add the directional transitions
1164  tod_color acol = atod.color + color_adjust_;
1165  lt += image::get_light_string(d, acol.r, acol.g, acol.b);
1166  }
1167 
1168  if(lt.empty()){
1169  tod_color col = tod.color + color_adjust_;
1170  if(!col.is_zero()){
1171  // no real lightmap needed but still color the hex
1172  lt = image::get_light_string(-1, col.r, col.g, col.b);
1173  }
1174  }
1175 
1176  if(terrains != nullptr) {
1177  // Cache the offmap name.
1178  // Since it is themeable it can change,
1179  // so don't make it static.
1180  const std::string off_map_name = "terrain/" + theme_.border().tile_image;
1181  for(std::vector<animated<image::locator> >::const_iterator it =
1182  terrains->begin(); it != terrains->end(); ++it) {
1183 
1184  const image::locator &image = animate_map_ ?
1185  it->get_current_frame() : it->get_first_frame();
1186 
1187  // We prevent ToD coloring and brightening of off-map tiles,
1188  // We need to test for the tile to be rendered and
1189  // not the location, since the transitions are rendered
1190  // over the offmap-terrain and these need a ToD coloring.
1191 #ifdef SDL_GPU
1192  sdl::timage img;
1193  const bool off_map = image.get_filename() == off_map_name;
1194  if(!use_local_light || off_map) {
1195  img = image::get_texture(image, off_map ? image::SCALED_TO_HEX : image_type);
1196  } else if(lt.empty()) {
1197  img = image::get_texture(image, image::SCALED_TO_HEX);
1198  } else {
1199  img = image::get_lighted_texture(image, lt, image::SCALED_TO_HEX);
1200  }
1201 
1202  if (!img.null()) {
1203  res.push_back(img);
1204  }
1205 #else
1206  surface surf;
1207  const bool off_map = image.get_filename() == off_map_name;
1208  if(off_map) {
1209  surf = image::get_image(image, off_map ? image::SCALED_TO_HEX : image_type);
1210  } else if(lt.empty()) {
1211  surf = image::get_image(image, image::SCALED_TO_HEX);
1212  } else {
1214  }
1215 
1216  if (!surf.null()) {
1217  res.push_back(surf);
1218  }
1219 #endif
1220  }
1221  }
1222 
1223  return res;
1224 }
1225 
1226 #ifdef SDL_GPU
1228  const map_location& loc, int x, int y,
1229  const sdl::timage &img)
1230 {
1231  drawing_buffer_.push_back(tblit(layer, loc, x, y, img));
1232 }
1233 
1235  const map_location& loc, int x, int y,
1236  const std::vector<sdl::timage> &imgs)
1237 {
1238  drawing_buffer_.push_back(tblit(layer, loc, x, y, imgs));
1239 }
1240 #else
1242  const map_location& loc, int x, int y, const surface& surf,
1243  const SDL_Rect &clip)
1244 {
1245  drawing_buffer_.push_back(tblit(layer, loc, x, y, surf, clip));
1246 }
1247 
1249  const map_location& loc, int x, int y,
1250  const std::vector<surface> &surf,
1251  const SDL_Rect &clip)
1252 {
1253  drawing_buffer_.push_back(tblit(layer, loc, x, y, surf, clip));
1254 }
1255 #endif
1256 
1257 // FIXME: temporary method. Group splitting should be made
1258 // public into the definition of tdrawing_layer
1259 //
1260 // The drawing is done per layer_group, the range per group is [low, high).
1265  // Make sure the movement doesn't show above fog and reachmap.
1268 };
1269 
1270 // no need to change this if layer_groups above is changed
1272 
1273 enum {
1274  // you may adjust the following when needed:
1275 
1276  // maximum border. 3 should be safe even if a larger border is in use somewhere
1278 
1279  // store x, y, and layer in one 32 bit integer
1280  // 4 most significant bits == layer group => 16
1282 
1283  // 10 second most significant bits == y => 1024
1285 
1286  // 1 third most significant bit == x parity => 2
1288 
1289  // 8 fourth most significant bits == layer => 256
1291 
1292  // 9 least significant bits == x / 2 => 512 (really 1024 for x)
1294 };
1295 
1297  : key_(0)
1298 {
1299  // max_layer_group + 1 is the last valid entry in layer_groups, but it is always > layer
1300  // thus the first --g is a given => start with max_layer_groups right away
1301  unsigned int g = max_layer_group;
1302  while (layer < layer_groups[g]) {
1303  --g;
1304  }
1305 
1306  enum {
1307  SHIFT_LAYER = BITS_FOR_X_OVER_2,
1308  SHIFT_X_PARITY = BITS_FOR_LAYER + SHIFT_LAYER,
1309  SHIFT_Y = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
1310  SHIFT_LAYER_GROUP = BITS_FOR_Y + SHIFT_Y
1311  };
1312  static_assert(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key_) * 8, "Bit field too small");
1313 
1314  // the parity of x must be more significant than the layer but less significant than y.
1315  // Thus basically every row is split in two: First the row containing all the odd x
1316  // then the row containing all the even x. Since thus the least significant bit of x is
1317  // not required for x ordering anymore it can be shifted out to the right.
1318  const unsigned int x_parity = static_cast<unsigned int>(loc.x) & 1;
1320  key_ |= (x_parity << SHIFT_X_PARITY);
1321  key_ |= (static_cast<unsigned int>(layer) << SHIFT_LAYER) | static_cast<unsigned int>(loc.x + MAX_BORDER) / 2;
1322 }
1323 
1325 {
1326  // std::list::sort() is a stable sort
1327  drawing_buffer_.sort();
1328 
1329 #ifdef SDL_GPU
1330  SDL_Rect clip_rect = map_area();
1331  GPU_SetClip(get_render_target(), clip_rect.x, clip_rect.y, clip_rect.w, clip_rect.h);
1332 
1333  /*
1334  * Info regarding the rendering algorithm.
1335  *
1336  * In order to render a hex properly it needs to be rendered per row. On
1337  * this row several layers need to be drawn at the same time. Mainly the
1338  * unit and the background terrain. This is needed since both can spill
1339  * in the next hex. The foreground terrain needs to be drawn before to
1340  * avoid decapitation a unit.
1341  *
1342  * This ended in the following priority order:
1343  * layergroup > location > layer > 'tblit' > surface
1344  */
1345 
1346  for (tblit &blit : drawing_buffer_) {
1347  for (sdl::timage& img : blit.images()) {
1348  if (!img.null()) {
1349  screen_.draw_texture(img, blit.x(), blit.y());
1350  }
1351  }
1352  }
1354  GPU_UnsetClip(get_render_target());
1355 #else
1356  SDL_Rect clip_rect = map_area();
1358  clip_rect_setter set_clip_rect(screen, &clip_rect);
1359 
1360  /*
1361  * Info regarding the rendering algorithm.
1362  *
1363  * In order to render a hex properly it needs to be rendered per row. On
1364  * this row several layers need to be drawn at the same time. Mainly the
1365  * unit and the background terrain. This is needed since both can spill
1366  * in the next hex. The foreground terrain needs to be drawn before to
1367  * avoid decapitation a unit.
1368  *
1369  * This ended in the following priority order:
1370  * layergroup > location > layer > 'tblit' > surface
1371  */
1372 
1373  for (const tblit &blit : drawing_buffer_) {
1374  for (const surface& surf : blit.surf()) {
1375  // Note that dstrect can be changed by sdl_blit
1376  // and so a new instance should be initialized
1377  // to pass to each call to sdl_blit.
1378  SDL_Rect dstrect = sdl::create_rect(blit.x(), blit.y(), 0, 0);
1379  SDL_Rect srcrect = blit.clip();
1380  SDL_Rect *srcrectArg = (srcrect.x | srcrect.y | srcrect.w | srcrect.h)
1381  ? &srcrect : nullptr;
1382  sdl_blit(surf, srcrectArg, screen, &dstrect);
1383  //NOTE: the screen part should already be marked as 'to update'
1384  }
1385  }
1387 #endif
1388 }
1389 
1391 {
1392  drawing_buffer_.clear();
1393 }
1394 
1395 void display::sunset(const size_t delay)
1396 {
1397  // This allow both parametric and toggle use
1398  sunset_delay = (sunset_delay == 0 && delay == 0) ? 3 : delay;
1399 }
1400 
1402 {
1403  benchmark = !benchmark;
1404 }
1405 
1407 {
1408  debug_foreground = !debug_foreground;
1409 }
1410 
1412 {
1413  if(video().faked()) {
1414  return;
1415  }
1416 
1417  surface& frameBuffer = get_video_surface();
1418 
1419  // This is just the debug function "sunset" to progressively darken the map area
1420  static size_t sunset_timer = 0;
1421  if (sunset_delay && ++sunset_timer > sunset_delay) {
1422  sunset_timer = 0;
1423  SDL_Rect r = map_outside_area(); // Use frameBuffer to also test the UI
1424  const Uint32 color = SDL_MapRGBA(video().getSurface()->format,0,0,0,SDL_ALPHA_OPAQUE);
1425  // Adjust the alpha if you want to balance cpu-cost / smooth sunset
1426  sdl::fill_rect_alpha(r, color, 1, frameBuffer);
1427  update_rect(r);
1428  }
1429 #ifdef SDL_GPU
1431 #else
1432  font::draw_floating_labels(frameBuffer);
1433 #endif
1435 
1436  video().flip();
1437 
1439 #ifdef SDL_GPU
1441 #else
1442  font::undraw_floating_labels(frameBuffer);
1443 #endif
1444 }
1445 
1447 {
1448  if (screen_.update_locked()) {
1449  return;
1450  }
1451 
1452  if(preferences::show_fps() || benchmark) {
1453  static int last_sample = SDL_GetTicks();
1454  static int frames = 0;
1455  ++frames;
1456  const int sample_freq = 10;
1457  if(frames == sample_freq) {
1458  const int this_sample = SDL_GetTicks();
1459 
1460  const int fps = (frames*1000)/(this_sample - last_sample);
1461  last_sample = this_sample;
1462  frames = 0;
1463 
1464  if(fps_handle_ != 0) {
1466  fps_handle_ = 0;
1467  }
1468  std::ostringstream stream;
1469  stream << "fps: " << fps;
1470  if (game_config::debug) {
1471  stream << "\nhex: " << drawn_hexes_*1.0/sample_freq;
1473  stream << " (" << (invalidated_hexes_-drawn_hexes_)*1.0/sample_freq << ")";
1474  }
1475  drawn_hexes_ = 0;
1476  invalidated_hexes_ = 0;
1477 
1478  font::floating_label flabel(stream.str());
1479  flabel.set_font_size(12);
1480  flabel.set_color(benchmark ? font::BAD_COLOR : font::NORMAL_COLOR);
1481  flabel.set_position(10, 100);
1482  flabel.set_alignment(font::LEFT_ALIGN);
1483 
1485  }
1486  } else if(fps_handle_ != 0) {
1488  fps_handle_ = 0;
1489  drawn_hexes_ = 0;
1490  invalidated_hexes_ = 0;
1491  }
1492 
1493  flip();
1494 }
1495 #ifdef SDL_GPU
1496 static void draw_panel(surface &target, const theme::panel& panel, std::vector<gui::button>& /*buttons*/)
1497 #else
1498 static void draw_panel(CVideo &video, const theme::panel& panel, std::vector<std::shared_ptr<gui::button>>& /*buttons*/)
1499 #endif
1500 {
1501  //log_scope("draw panel");
1502  DBG_DP << "drawing panel " << panel.get_id() << "\n";
1503 
1504  surface surf(image::get_image(panel.image()));
1505 
1506  const SDL_Rect screen = screen_area();
1507  SDL_Rect& loc = panel.location(screen);
1508 
1509  DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y
1510  << ", w=" << loc.w << ", h=" << loc.h << "\n";
1511 
1512  if(!surf.null()) {
1513  if(surf->w != loc.w || surf->h != loc.h) {
1514  surf.assign(tile_surface(surf,loc.w,loc.h));
1515  }
1516 #ifdef SDL_GPU
1517  blit_surface(surf, nullptr, target, &loc);
1518 #else
1519  video.blit_surface(loc.x, loc.y, surf);
1520  update_rect(loc);
1521 #endif
1522  }
1523 }
1524 
1525 static void draw_label(CVideo& video, surface target, const theme::label& label)
1526 {
1527  //log_scope("draw label");
1528 
1529  Uint32 RGB=label.font_rgb();
1530  int red = (RGB & 0x00FF0000)>>16;
1531  int green = (RGB & 0x0000FF00)>>8;
1532  int blue = (RGB & 0x000000FF);
1533 
1534  std::string c_start="<";
1535  std::string c_sep=",";
1536  std::string c_end=">";
1537  std::stringstream color;
1538  color<< c_start << red << c_sep << green << c_sep << blue << c_end;
1539  std::string text = label.text();
1540 
1541  if(label.font_rgb_set()) {
1542  color<<text;
1543  text = color.str();
1544  }
1545  const std::string& icon = label.icon();
1546  SDL_Rect& loc = label.location(screen_area());
1547 
1548  if(icon.empty() == false) {
1549  surface surf(image::get_image(icon));
1550  if(!surf.null()) {
1551  if(surf->w > loc.w || surf->h > loc.h) {
1552  surf.assign(scale_surface(surf,loc.w,loc.h));
1553  }
1554 
1555  sdl_blit(surf,nullptr,target,&loc);
1556  }
1557 
1558  if(text.empty() == false) {
1559  tooltips::add_tooltip(loc,text);
1560  }
1561  } else if(text.empty() == false) {
1562  font::draw_text(&video,loc,label.font_size(),font::NORMAL_COLOR,text,loc.x,loc.y);
1563  }
1564 
1565  update_rect(loc);
1566 }
1567 
1569 {
1570 #ifdef SDL_GPU
1571  const surface& screen(screen_.getSurface());
1572 
1573  /*
1574  * The minimap is also a panel, force it to update its contents.
1575  * This is required when the size of the minimap has been modified.
1576  */
1578 
1579  draw_panel_image();
1580 
1581  const std::vector<theme::label>& labels = theme_.labels();
1582  for(std::vector<theme::label>::const_iterator i = labels.begin(); i != labels.end(); ++i) {
1583  draw_label(video(),screen,*i);
1584  }
1585  render_buttons();
1586 #else
1588 
1589  /*
1590  * The minimap is also a panel, force it to update its contents.
1591  * This is required when the size of the minimap has been modified.
1592  */
1594 
1595  const std::vector<theme::panel>& panels = theme_.panels();
1596  for(std::vector<theme::panel>::const_iterator p = panels.begin(); p != panels.end(); ++p) {
1598  }
1599 
1600  const std::vector<theme::label>& labels = theme_.labels();
1601  for(std::vector<theme::label>::const_iterator i = labels.begin(); i != labels.end(); ++i) {
1602  draw_label(video(),screen,*i);
1603  }
1604 
1605  render_buttons();
1606 #endif
1607 }
1608 
1609 #ifdef SDL_GPU
1610 void display::draw_panel_image(SDL_Rect *clip)
1611 {
1612  surface screen(video().getSurface());
1613 
1614  if (update_panel_image_) {
1616  const std::vector<theme::panel>& panels = theme_.panels();
1617  for(std::vector<theme::panel>::const_iterator p = panels.begin(); p != panels.end(); ++p) {
1618  draw_panel(surf, *p, menu_buttons_);
1619  }
1620 
1621  panel_image_ = sdl::timage(surf);
1622  update_panel_image_ = false;
1623  }
1624 
1625  if (clip != nullptr)
1626  GPU_SetClip(get_render_target(), clip->x, clip->y, clip->w, clip->h);
1627 
1628  video().draw_texture(panel_image_, 0, 0);
1629 
1630  if (clip != nullptr)
1631  GPU_UnsetClip(get_render_target());
1632 }
1633 #endif
1634 
1635 #ifdef SDL_GPU
1636 static void draw_background(CVideo &video, const SDL_Rect& area, const std::string& image)
1637 {
1638  sdl::timage img(image::get_texture(image));
1639  if (img.null()) {
1640  return;
1641  }
1642  img.set_wrap(GPU_WRAP_REPEAT, GPU_WRAP_REPEAT);
1643  img.set_clip(sdl::create_rect(0, 0, area.w, area.h));
1644  video.draw_texture(img, area.x, area.y);
1645 }
1646 #else
1647 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
1648 {
1649  const surface background(image::get_image(image));
1650  if(background.null()) {
1651  return;
1652  }
1653  const unsigned int width = background->w;
1654  const unsigned int height = background->h;
1655 
1656  const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
1657  const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
1658 
1659  for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
1660  for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
1661  SDL_Rect clip = sdl::create_rect(w_off, h_off, 0, 0);
1662  sdl_blit(background, nullptr, screen, &clip);
1663  }
1664  }
1665 }
1666 #endif
1667 
1669  const tdrawing_layer layer, const std::string& text,
1670  size_t font_size, SDL_Color color, double x_in_hex, double y_in_hex)
1671 {
1672  if (text.empty()) return;
1673 
1674  const size_t font_sz = static_cast<size_t>(font_size * get_zoom_factor());
1675 
1676  surface text_surf = font::get_rendered_text(text, font_sz, color);
1677  surface back_surf = font::get_rendered_text(text, font_sz, font::BLACK_COLOR);
1678  const int x = get_location_x(loc) - text_surf->w/2
1679  + static_cast<int>(x_in_hex* hex_size());
1680  const int y = get_location_y(loc) - text_surf->h/2
1681  + static_cast<int>(y_in_hex* hex_size());
1682 #ifdef SDL_GPU
1683  sdl::timage text_img(text_surf);
1684  sdl::timage back_img(back_surf);
1685 
1686  for (int dy=-1; dy <= 1; ++dy) {
1687  for (int dx=-1; dx <= 1; ++dx) {
1688  if (dx!=0 || dy!=0) {
1689  drawing_buffer_add(layer, loc, x + dx, y + dy, back_img);
1690  }
1691  }
1692  }
1693  drawing_buffer_add(layer, loc, x, y, text_img);
1694 #else
1695  for (int dy=-1; dy <= 1; ++dy) {
1696  for (int dx=-1; dx <= 1; ++dx) {
1697  if (dx!=0 || dy!=0) {
1698  drawing_buffer_add(layer, loc, x + dx, y + dy, back_surf);
1699  }
1700  }
1701  }
1702  drawing_buffer_add(layer, loc, x, y, text_surf);
1703 #endif
1704 }
1705 
1706 //TODO: convert this to use sdl::ttexture
1707 #ifdef SDL_GPU
1708 void display::render_image(int x, int y, const display::tdrawing_layer drawing_layer,
1709  const map_location& loc, sdl::timage image,
1710  bool hreverse, bool greyscale, fixed_t /*alpha*/,
1711  Uint32 /*blendto*/, double /*blend_ratio*/, double submerged, bool vreverse)
1712 {
1713  int effect_flags = hreverse ? SHADER_EFFECT_FLIP : SHADER_EFFECT_NONE |
1714  vreverse ? SHADER_EFFECT_FLOP : SHADER_EFFECT_NONE |
1715  greyscale ? SHADER_EFFECT_GRAYSCALE : SHADER_EFFECT_NONE;
1716 
1717  image.set_effects(effect_flags);
1718  image.set_submerge(submerged);
1719 
1720  drawing_buffer_add(drawing_layer, loc, x, y, image);
1721 }
1722 
1723 #else
1724 void display::render_image(int x, int y, const display::tdrawing_layer drawing_layer,
1725  const map_location& loc, surface image,
1726  bool hreverse, bool greyscale, fixed_t alpha,
1727  Uint32 blendto, double blend_ratio, double submerged, bool vreverse)
1728 {
1729  if (image==nullptr)
1730  return;
1731 
1732  SDL_Rect image_rect = sdl::create_rect(x, y, image->w, image->h);
1733  SDL_Rect clip_rect = map_area();
1734  if (!sdl::rects_overlap(image_rect, clip_rect))
1735  return;
1736 
1737  surface surf(image);
1738 
1739  if(hreverse) {
1740  surf = image::reverse_image(surf);
1741  }
1742  if(vreverse) {
1743  surf = flop_surface(surf, false);
1744  }
1745 
1746  if(greyscale) {
1747  surf = greyscale_image(surf, false);
1748  }
1749 
1750  if(blend_ratio != 0) {
1751  surf = blend_surface(surf, blend_ratio, blendto, false);
1752  }
1753  if(alpha > ftofxp(1.0)) {
1754  surf = brighten_image(surf, alpha, false);
1755  //} else if(alpha != 1.0 && blendto != 0) {
1756  // surf.assign(blend_surface(surf,1.0-alpha,blendto));
1757  } else if(alpha != ftofxp(1.0)) {
1758  surf = adjust_surface_alpha(surf, alpha, false);
1759  }
1760 
1761  if(surf == nullptr) {
1762  ERR_DP << "surface lost..." << std::endl;
1763  return;
1764  }
1765 
1766  if(submerged > 0.0) {
1767  // divide the surface into 2 parts
1768  const int submerge_height = std::max<int>(0, surf->h*(1.0-submerged));
1769  const int depth = surf->h - submerge_height;
1770  SDL_Rect srcrect = sdl::create_rect(0, 0, surf->w, submerge_height);
1771  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1772 
1773  if(submerge_height != surf->h) {
1774  //the lower part will be transparent
1775  float alpha_base = 0.3f; // 30% alpha at surface of water
1776  float alpha_delta = 0.015f; // lose 1.5% per pixel depth
1777  alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
1778  surf = submerge_alpha(surf, depth, alpha_base, alpha_delta, false);
1779 
1780  srcrect.y = submerge_height;
1781  srcrect.h = surf->h-submerge_height;
1782  y += submerge_height;
1783 
1784  drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
1785  }
1786  } else {
1787  // simple blit
1788  drawing_buffer_add(drawing_layer, loc, x, y, surf);
1789  }
1790 }
1791 #endif
1793 {
1795  selectedHex_ = hex;
1798 }
1799 
1801 {
1803  mouseoverHex_ = hex;
1805 }
1806 
1808 {
1809  if(diagnostic_label_ != 0) {
1811  diagnostic_label_ = 0;
1812  }
1813 
1814  if(msg != "") {
1815  font::floating_label flabel(msg);
1817  flabel.set_color(font::YELLOW_COLOR);
1818  flabel.set_position(300, 50);
1819  flabel.set_clip_rect(map_outside_area());
1820 
1822  }
1823 }
1824 
1826 {
1827  if (get_map().empty()) {
1828  return;
1829  }
1830 
1831  if(benchmark) {
1832  invalidateAll_ = true;
1833  }
1834 
1835  if(!panelsDrawn_) {
1836  draw_all_panels();
1837  panelsDrawn_ = true;
1838  }
1839 
1840  if(redraw_background_) {
1841  // Full redraw of the background
1842  const SDL_Rect clip_rect = map_outside_area();
1843 #ifdef SDL_GPU
1845 #else
1846  const surface& screen = get_screen_surface();
1847  clip_rect_setter set_clip_rect(screen, &clip_rect);
1848  draw_background(screen, clip_rect, theme_.border().background_image);
1849 #endif
1850  update_rect(clip_rect);
1851 
1852  redraw_background_ = false;
1853 
1854  // Force a full map redraw
1855  invalidateAll_ = true;
1856  }
1857 
1858  if(invalidateAll_) {
1859  DBG_DP << "draw() with invalidateAll\n";
1860 
1861  // toggle invalidateAll_ first to allow regular invalidations
1862  invalidateAll_ = false;
1864 
1865  redrawMinimap_ = true;
1866  }
1867 }
1868 
1869 void display::draw_wrap(bool update, bool force)
1870 {
1871  static const int time_between_draws = preferences::draw_delay();
1872  const int current_time = SDL_GetTicks();
1873  const int wait_time = nextDraw_ - current_time;
1874 
1875  if(redrawMinimap_) {
1876  redrawMinimap_ = false;
1877  draw_minimap();
1878  }
1879 
1880  if(update) {
1881  update_display();
1882  if(!force && !benchmark && wait_time > 0) {
1883  // If it's not time yet to draw, delay until it is
1884  SDL_Delay(wait_time);
1885  }
1886 
1887  // Set the theoretical next draw time
1888  nextDraw_ += time_between_draws;
1889 
1890  // If the next draw already should have been finished,
1891  // we'll enter an update frenzy, so make sure that the
1892  // too late value doesn't keep growing.
1893  // Note: if force is used too often,
1894  // we can also get the opposite effect.
1895  nextDraw_ = std::max<int>(nextDraw_, SDL_GetTicks());
1896  }
1897 }
1898 
1900 {
1901  for(std::vector<std::shared_ptr<gui::button>>::iterator i = action_buttons_.begin();
1902  i != action_buttons_.end(); ++i) {
1903  if((*i)->pressed()) {
1904  const size_t index = i - action_buttons_.begin();
1905  if(index >= theme_.actions().size()) {
1906  assert(false);
1907  return nullptr;
1908  }
1909  return &theme_.actions()[index];
1910  }
1911  }
1912 
1913  return nullptr;
1914 }
1915 
1917 {
1918  for(std::vector<std::shared_ptr<gui::button>>::iterator i = menu_buttons_.begin(); i != menu_buttons_.end(); ++i) {
1919  if((*i)->pressed()) {
1920  const size_t index = i - menu_buttons_.begin();
1921  if(index >= theme_.menus().size()) {
1922  assert(false);
1923  return nullptr;
1924  }
1925  return theme_.get_menu_item((*i)->id());
1926  }
1927  }
1928 
1929  return nullptr;
1930 }
1931 
1932 void display::enable_menu(const std::string& item, bool enable)
1933 {
1934  for(std::vector<theme::menu>::const_iterator menu = theme_.menus().begin();
1935  menu != theme_.menus().end(); ++menu) {
1936 
1937  std::vector<std::string>::const_iterator hasitem =
1938  std::find(menu->items().begin(), menu->items().end(), item);
1939 
1940  if(hasitem != menu->items().end()) {
1941  const size_t index = menu - theme_.menus().begin();
1942  if(index >= menu_buttons_.size()) {
1943  continue;
1944  }
1945  menu_buttons_[index]->enable(enable);
1946  }
1947  }
1948 }
1949 
1950 void display::announce(const std::string& message, const SDL_Color& color)
1951 {
1952  font::floating_label flabel(message);
1954  flabel.set_color(color);
1956  flabel.set_lifetime(100);
1957  flabel.set_clip_rect(map_outside_area());
1958 
1959  font::add_floating_label(flabel);
1960 }
1961 
1962 
1963 void display::draw_border(const map_location& loc, const int xpos, const int ypos)
1964 {
1965 #ifdef SDL_GPU
1966  /**
1967  * at the moment the border must be between 0.0 and 0.5
1968  * and the image should always be prepared for a 0.5 border.
1969  * This way this code doesn't need modifications for other border sizes.
1970  */
1971 
1972  // First handle the corners :
1973  if(loc.x == -1 && loc.y == -1) { // top left corner
1974  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
1975  image::get_texture(theme_.border().corner_image_top_left, image::SCALED_TO_ZOOM));
1976  } else if(loc.x == get_map().w() && loc.y == -1) { // top right corner
1977  // We use the map idea of odd and even, and map coords are internal coords + 1
1978  if(loc.x%2 == 0) {
1979  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
1981  } else {
1982  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
1984  }
1985  } else if(loc.x == -1 && loc.y == get_map().h()) { // bottom left corner
1986  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
1988 
1989  } else if(loc.x == get_map().w() && loc.y == get_map().h()) { // bottom right corner
1990  // We use the map idea of odd and even, and map coords are internal coords + 1
1991  if(loc.x%2 == 1) {
1992  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
1994  } else {
1995  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
1997  }
1998 
1999  // Now handle the sides:
2000  } else if(loc.x == -1) { // left side
2001  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2002  image::get_texture(theme_.border().border_image_left, image::SCALED_TO_ZOOM));
2003  } else if(loc.x == get_map().w()) { // right side
2004  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2005  image::get_texture(theme_.border().border_image_right, image::SCALED_TO_ZOOM));
2006  } else if(loc.y == -1) { // top side
2007  // We use the map idea of odd and even, and map coords are internal coords + 1
2008  if(loc.x%2 == 1) {
2009  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2010  image::get_texture(theme_.border().border_image_top_even, image::SCALED_TO_ZOOM));
2011  } else {
2012  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
2013  image::get_texture(theme_.border().border_image_top_odd, image::SCALED_TO_ZOOM));
2014  }
2015  } else if(loc.y == get_map().h()) { // bottom side
2016  // We use the map idea of odd and even, and map coords are internal coords + 1
2017  if(loc.x%2 == 1) {
2018  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2020  } else {
2021  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
2023  }
2024  }
2025 #else
2026  /**
2027  * at the moment the border must be between 0.0 and 0.5
2028  * and the image should always be prepared for a 0.5 border.
2029  * This way this code doesn't need modifications for other border sizes.
2030  */
2031 
2032  // First handle the corners :
2033  if(loc.x == -1 && loc.y == -1) { // top left corner
2034  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2036  } else if(loc.x == get_map().w() && loc.y == -1) { // top right corner
2037  // We use the map idea of odd and even, and map coords are internal coords + 1
2038  if(loc.x%2 == 0) {
2039  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
2041  } else {
2042  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2044  }
2045  } else if(loc.x == -1 && loc.y == get_map().h()) { // bottom left corner
2046  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2048 
2049  } else if(loc.x == get_map().w() && loc.y == get_map().h()) { // bottom right corner
2050  // We use the map idea of odd and even, and map coords are internal coords + 1
2051  if(loc.x%2 == 1) {
2052  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2054  } else {
2055  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2057  }
2058 
2059  // Now handle the sides:
2060  } else if(loc.x == -1) { // left side
2061  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2063  } else if(loc.x == get_map().w()) { // right side
2064  drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
2066  } else if(loc.y == -1) { // top side
2067  // We use the map idea of odd and even, and map coords are internal coords + 1
2068  if(loc.x%2 == 1) {
2069  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2071  } else {
2072  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
2074  }
2075  } else if(loc.y == get_map().h()) { // bottom side
2076  // We use the map idea of odd and even, and map coords are internal coords + 1
2077  if(loc.x%2 == 1) {
2078  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
2080  } else {
2081  drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
2083  }
2084  }
2085 #endif
2086 }
2087 
2089 {
2090  const SDL_Rect& area = minimap_area();
2091 
2092  if(area.w == 0 || area.h == 0) {
2093  return;
2094  }
2095 
2096 #ifdef SDL_GPU
2097  minimap_location_ = image::draw_minimap(screen_, area, get_map(), &dc_->teams()[currentTeam_], nullptr);
2098 
2100 #else
2101  if(minimap_ == nullptr || minimap_->w > area.w || minimap_->h > area.h) {
2102  minimap_ = image::getMinimap(area.w, area.h, get_map(), &dc_->teams()[currentTeam_], (selectedHex_.valid() && !is_blindfolded()) ? &reach_map_ : nullptr);
2103  if(minimap_ == nullptr) {
2104  return;
2105  }
2106  }
2107 
2108  const surface& screen(screen_.getSurface());
2109  clip_rect_setter clip_setter(screen, &area);
2110 
2111  SDL_Color back_color = {31,31,23,SDL_ALPHA_OPAQUE};
2112  draw_centered_on_background(minimap_, area, back_color, screen);
2113 
2114  //update the minimap location for mouse and units functions
2115  minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
2116  minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
2117  minimap_location_.w = minimap_->w;
2118  minimap_location_.h = minimap_->h;
2119 
2121 
2122  // calculate the visible portion of the map:
2123  // scaling between minimap and full map images
2124  double xscaling = 1.0*minimap_->w / (get_map().w()*hex_width());
2125  double yscaling = 1.0*minimap_->h / (get_map().h()*hex_size());
2126 
2127  // we need to shift with the border size
2128  // and the 0.25 from the minimap balanced drawing
2129  // and the possible difference between real map and outside off-map
2130  SDL_Rect map_rect = map_area();
2131  SDL_Rect map_out_rect = map_outside_area();
2132  double border = theme_.border().size;
2133  double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
2134  double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
2135 
2136  int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
2137  int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
2138  int view_w = static_cast<int>(map_out_rect.w * xscaling);
2139  int view_h = static_cast<int>(map_out_rect.h * yscaling);
2140 
2141  const Uint32 box_color = SDL_MapRGB(minimap_->format,0xFF,0xFF,0xFF);
2142  sdl::draw_rectangle(minimap_location_.x + view_x - 1,
2143  minimap_location_.y + view_y - 1,
2144  view_w + 2, view_h + 2,
2145  box_color, screen);
2146 #endif
2147 }
2148 
2150 {
2151  if (!preferences::minimap_draw_units() || is_blindfolded()) return;
2152 
2153  double xscaling = 1.0 * minimap_location_.w / get_map().w();
2154  double yscaling = 1.0 * minimap_location_.h / get_map().h();
2155 
2156  for(unit_map::const_iterator u = dc_->units().begin(); u != dc_->units().end(); ++u) {
2157  if (fogged(u->get_location()) ||
2158  (dc_->teams()[currentTeam_].is_enemy(u->side()) &&
2159  u->invisible(u->get_location())) ||
2160  u->get_hidden()) {
2161  continue;
2162  }
2163 
2164  int side = u->side();
2165  SDL_Color col = team::get_minimap_color(side);
2166 
2168 
2169  if (dc_->teams()[currentTeam_].is_enemy(side)) {
2171  } else {
2172 
2173  if (currentTeam_ +1 == static_cast<unsigned>(side)) {
2174 
2175  if (u->movement_left() == u->total_movement())
2177  else if (u->movement_left() == 0)
2179  else
2181 
2182  } else
2184  }
2185  }
2186 
2187  double u_x = u->get_location().x * xscaling;
2188  double u_y = (u->get_location().y + (is_odd(u->get_location().x) ? 1 : -1)/4.0) * yscaling;
2189  // use 4/3 to compensate the horizontal hexes imbrication
2190  double u_w = 4.0 / 3.0 * xscaling;
2191  double u_h = yscaling;
2192 
2193  SDL_Rect r = sdl::create_rect(minimap_location_.x + round_double(u_x)
2194  , minimap_location_.y + round_double(u_y)
2195  , round_double(u_w)
2196  , round_double(u_h));
2197 #ifdef SDL_GPU
2198  sdl::fill_rect(screen_, r, col);
2199 #else
2200  const Uint32 mapped_col = SDL_MapRGB(video().getSurface()->format,col.r,col.g,col.b);
2201  sdl::fill_rect(video().getSurface(), &r, mapped_col);
2202 #endif
2203  }
2204 }
2205 
2206 bool display::scroll(int xmove, int ymove, bool force)
2207 {
2208  if(view_locked_ && !force) {
2209  return false;
2210  }
2211 
2212  const int orig_x = xpos_;
2213  const int orig_y = ypos_;
2214  xpos_ += xmove;
2215  ypos_ += ymove;
2217  const int dx = orig_x - xpos_; // dx = -xmove
2218  const int dy = orig_y - ypos_; // dy = -ymove
2219 
2220  // Only invalidate if we've actually moved
2221  if(dx == 0 && dy == 0)
2222  return false;
2223 
2226 
2228 
2229  SDL_Rect dstrect = map_area();
2230  dstrect.x += dx;
2231  dstrect.y += dy;
2232  dstrect = sdl::intersect_rects(dstrect, map_area());
2233 
2234  SDL_Rect srcrect = dstrect;
2235  srcrect.x -= dx;
2236  srcrect.y -= dy;
2237  if (!screen_.update_locked()) {
2238 
2239  /* TODO: This is a workaround for a SDL2 bug when blitting on overlapping surfaces.
2240  * The bug only strikes during scrolling, but will then duplicate textures across
2241  * the entire map. */
2242  surface screen_copy = make_neutral_surface(screen);
2243  SDL_SetSurfaceBlendMode(screen_copy, SDL_BLENDMODE_NONE);
2244  SDL_BlitSurface(screen_copy,&srcrect,screen,&dstrect);
2245  }
2246 
2247  // Invalidate locations in the newly visible rects
2248 
2249  if (dy != 0) {
2250  SDL_Rect r = map_area();
2251  if(dy < 0)
2252  r.y = r.y + r.h + dy;
2253  r.h = abs(dy);
2255  }
2256  if (dx != 0) {
2257  SDL_Rect r = map_area();
2258  if (dx < 0)
2259  r.x = r.x + r.w + dx;
2260  r.w = abs(dx);
2262  }
2264 #ifdef SDL_GPU
2265  screen_.clear_overlay();
2266  invalidate_all();
2267 #else
2268  update_rect(map_area());
2269 #endif
2270 
2271  redrawMinimap_ = true;
2272  return true;
2273 }
2274 
2276 {
2277  return zoom_ == MaxZoom;
2278 }
2279 
2281 {
2282  return zoom_ == MinZoom;
2283 }
2284 
2285 bool display::set_zoom(int amount, bool absolute)
2286 {
2287  int new_zoom = zoom_ + amount;
2288  if (absolute)
2289  new_zoom = amount;
2290  if (new_zoom < MinZoom) {
2291  new_zoom = MinZoom;
2292  }
2293  if (new_zoom > MaxZoom) {
2294  new_zoom = MaxZoom;
2295  }
2296  LOG_DP << "new_zoom = " << new_zoom << std::endl;
2297  if (new_zoom != zoom_) {
2298  std::shared_ptr<gui::slider> zoom_slider = find_slider("map-zoom-slider");
2299  if (zoom_slider) {
2300  zoom_slider->set_value(new_zoom);
2301  }
2302  SDL_Rect const &area = map_area();
2303  xpos_ += (xpos_ + area.w / 2) * (absolute ? new_zoom - zoom_ : amount) / zoom_;
2304  ypos_ += (ypos_ + area.h / 2) * (absolute ? new_zoom - zoom_ : amount) / zoom_;
2305 
2306  zoom_ = new_zoom;
2308  if (zoom_ != DefaultZoom) {
2309  last_zoom_ = zoom_;
2310  }
2312 
2314  redraw_background_ = true;
2315  invalidate_all();
2316 
2317  // Forces a redraw after zooming.
2318  // This prevents some graphic glitches from occurring.
2319  draw();
2320  return true;
2321  } else {
2322  return false;
2323  }
2324 }
2325 
2327 {
2328  if (zoom_ != DefaultZoom) {
2329  last_zoom_ = zoom_;
2330  set_zoom(DefaultZoom - zoom_ );
2331  } else {
2332  // When we are already at the default zoom,
2333  // switch to the last zoom used
2335  }
2336 }
2337 
2339 {
2340  int x = get_location_x(loc);
2341  int y = get_location_y(loc);
2342  return !outside_area(map_area(), x, y);
2343 }
2344 
2346 {
2347  int x = get_location_x(loc);
2348  int y = get_location_y(loc);
2349  const SDL_Rect &area = map_area();
2350  int hw = hex_width(), hs = hex_size();
2351  return x + hs >= area.x - hw && x < area.x + area.w + hw &&
2352  y + hs >= area.y - hs && y < area.y + area.h + hs;
2353 }
2354 
2355 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force)
2356 {
2357  if(!force && (view_locked_ || !preferences::scroll_to_action())) return;
2358  if(screen_.update_locked()) {
2359  return;
2360  }
2361  const SDL_Rect area = map_area();
2362  const int xmove_expected = screenxpos - (area.x + area.w/2);
2363  const int ymove_expected = screenypos - (area.y + area.h/2);
2364 
2365  int xpos = xpos_ + xmove_expected;
2366  int ypos = ypos_ + ymove_expected;
2367  bounds_check_position(xpos, ypos);
2368  int xmove = xpos - xpos_;
2369  int ymove = ypos - ypos_;
2370 
2371  if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
2372  scroll(xmove,ymove,true);
2373  draw();
2374  return;
2375  }
2376 
2377  // Doing an animated scroll, with acceleration etc.
2378 
2379  int x_old = 0;
2380  int y_old = 0;
2381 
2382  const double dist_total = hypot(xmove, ymove);
2383  double dist_moved = 0.0;
2384 
2385  int t_prev = SDL_GetTicks();
2386 
2387  double velocity = 0.0;
2388  while (dist_moved < dist_total) {
2389  events::pump();
2390 
2391  int t = SDL_GetTicks();
2392  double dt = (t - t_prev) / 1000.0;
2393  if (dt > 0.200) {
2394  // Do not skip too many frames on slow PCs
2395  dt = 0.200;
2396  }
2397  t_prev = t;
2398 
2399  const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
2400  const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
2401 
2402  double velocity_max = preferences::scroll_speed() * 60.0;
2403  velocity_max *= turbo_speed();
2404  double accel = velocity_max / accel_time;
2405  double decel = velocity_max / decel_time;
2406 
2407  // If we started to decelerate now, where would we stop?
2408  double stop_time = velocity / decel;
2409  double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
2410  if (dist_stop > dist_total || velocity > velocity_max) {
2411  velocity -= decel * dt;
2412  if (velocity < 1.0) velocity = 1.0;
2413  } else {
2414  velocity += accel * dt;
2415  if (velocity > velocity_max) velocity = velocity_max;
2416  }
2417 
2418  dist_moved += velocity * dt;
2419  if (dist_moved > dist_total) dist_moved = dist_total;
2420 
2421  int x_new = round_double(xmove * dist_moved / dist_total);
2422  int y_new = round_double(ymove * dist_moved / dist_total);
2423 
2424  int dx = x_new - x_old;
2425  int dy = y_new - y_old;
2426 
2427  scroll(dx,dy,true);
2428  x_old += dx;
2429  y_old += dy;
2430  draw();
2431  }
2432 }
2433 
2434 void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
2435 {
2436  if(get_map().on_board(loc) == false) {
2437  ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile." << std::endl;
2438  return;
2439  }
2440 
2441  std::vector<map_location> locs;
2442  locs.push_back(loc);
2443  scroll_to_tiles(locs, scroll_type, check_fogged,false,0.0,force);
2444 }
2445 
2447  SCROLL_TYPE scroll_type, bool check_fogged,
2448  double add_spacing, bool force)
2449 {
2450  std::vector<map_location> locs;
2451  locs.push_back(loc1);
2452  locs.push_back(loc2);
2453  scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing,force);
2454 }
2455 
2456 void display::scroll_to_tiles(const std::vector<map_location>::const_iterator & begin,
2457  const std::vector<map_location>::const_iterator & end,
2458  SCROLL_TYPE scroll_type, bool check_fogged,
2459  bool only_if_possible, double add_spacing, bool force)
2460 {
2461  // basically we calculate the min/max coordinates we want to have on-screen
2462  int minx = 0;
2463  int maxx = 0;
2464  int miny = 0;
2465  int maxy = 0;
2466  bool valid = false;
2467 
2468  for(std::vector<map_location>::const_iterator itor = begin; itor != end ; ++itor) {
2469  if(get_map().on_board(*itor) == false) continue;
2470  if(check_fogged && fogged(*itor)) continue;
2471 
2472  int x = get_location_x(*itor);
2473  int y = get_location_y(*itor);
2474 
2475  if (!valid) {
2476  minx = x;
2477  maxx = x;
2478  miny = y;
2479  maxy = y;
2480  valid = true;
2481  } else {
2482  int minx_new = std::min<int>(minx,x);
2483  int miny_new = std::min<int>(miny,y);
2484  int maxx_new = std::max<int>(maxx,x);
2485  int maxy_new = std::max<int>(maxy,y);
2486  SDL_Rect r = map_area();
2487  r.x = minx_new;
2488  r.y = miny_new;
2489  if(outside_area(r, maxx_new, maxy_new)) {
2490  // we cannot fit all locations to the screen
2491  if (only_if_possible) return;
2492  break;
2493  }
2494  minx = minx_new;
2495  miny = miny_new;
2496  maxx = maxx_new;
2497  maxy = maxy_new;
2498  }
2499  }
2500  //if everything is fogged or the location list is empty
2501  if(!valid) return;
2502 
2503  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2504  SDL_Rect r = map_area();
2505  int spacing = round_double(add_spacing*hex_size());
2506  r.x += spacing;
2507  r.y += spacing;
2508  r.w -= 2*spacing;
2509  r.h -= 2*spacing;
2510  if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
2511  return;
2512  }
2513  }
2514 
2515  // let's do "normal" rectangle math from now on
2516  SDL_Rect locs_bbox;
2517  locs_bbox.x = minx;
2518  locs_bbox.y = miny;
2519  locs_bbox.w = maxx - minx + hex_size();
2520  locs_bbox.h = maxy - miny + hex_size();
2521 
2522  // target the center
2523  int target_x = locs_bbox.x + locs_bbox.w/2;
2524  int target_y = locs_bbox.y + locs_bbox.h/2;
2525 
2526  if (scroll_type == ONSCREEN || scroll_type == ONSCREEN_WARP) {
2527  // when doing an ONSCREEN scroll we do not center the target unless needed
2528  SDL_Rect r = map_area();
2529  int map_center_x = r.x + r.w/2;
2530  int map_center_y = r.y + r.h/2;
2531 
2532  int h = r.h;
2533  int w = r.w;
2534 
2535  // we do not want to be only inside the screen rect, but center a bit more
2536  double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
2537  w = static_cast<int>(w * inside_frac);
2538  h = static_cast<int>(h * inside_frac);
2539 
2540  // shrink the rectangle by the size of the locations rectangle we found
2541  // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
2542  w -= locs_bbox.w;
2543  h -= locs_bbox.h;
2544 
2545  if (w < 1) w = 1;
2546  if (h < 1) h = 1;
2547 
2548  r.x = target_x - w/2;
2549  r.y = target_y - h/2;
2550  r.w = w;
2551  r.h = h;
2552 
2553  // now any point within r is a possible target to scroll to
2554  // we take the one with the minimum distance to map_center
2555  // which will always be at the border of r
2556 
2557  if (map_center_x < r.x) {
2558  target_x = r.x;
2559  target_y = map_center_y;
2560  if (target_y < r.y) target_y = r.y;
2561  if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
2562  } else if (map_center_x > r.x+r.w-1) {
2563  target_x = r.x+r.w-1;
2564  target_y = map_center_y;
2565  if (target_y < r.y) target_y = r.y;
2566  if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
2567  } else if (map_center_y < r.y) {
2568  target_y = r.y;
2569  target_x = map_center_x;
2570  if (target_x < r.x) target_x = r.x;
2571  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2572  } else if (map_center_y > r.y+r.h-1) {
2573  target_y = r.y+r.h-1;
2574  target_x = map_center_x;
2575  if (target_x < r.x) target_x = r.x;
2576  if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
2577  } else {
2578  ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all..." << std::endl;
2579  // keep the target at the center
2580  }
2581  }
2582 
2583  scroll_to_xy(target_x, target_y,scroll_type,force);
2584 }
2585 
2586 
2588 {
2589  const int orig_zoom = zoom_;
2590 
2591  if(zoom_ < MinZoom) {
2592  zoom_ = MinZoom;
2593  }
2594 
2595  if(zoom_ > MaxZoom) {
2596  zoom_ = MaxZoom;
2597  }
2598 
2600 
2601  if(zoom_ != orig_zoom) {
2603  }
2604 }
2605 
2606 void display::bounds_check_position(int& xpos, int& ypos)
2607 {
2608  const int tile_width = hex_width();
2609 
2610  // Adjust for the border 2 times
2611  const int xend = static_cast<int>(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3);
2612  const int yend = static_cast<int>(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2);
2613 
2614  if(xpos > xend - map_area().w) {
2615  xpos = xend - map_area().w;
2616  }
2617 
2618  if(ypos > yend - map_area().h) {
2619  ypos = yend - map_area().h;
2620  }
2621 
2622  if(xpos < 0) {
2623  xpos = 0;
2624  }
2625 
2626  if(ypos < 0) {
2627  ypos = 0;
2628  }
2629 }
2630 
2631 double display::turbo_speed() const
2632 {
2633  bool res = turbo_;
2634  if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
2635  res = !res;
2636  }
2637 
2638  res |= screen_.faked();
2639  if (res)
2640  return turbo_speed_;
2641  else
2642  return 1.0;
2643 }
2644 
2646 {
2647  idle_anim_rate_ = std::pow(2.0, -rate/10.0);
2648 }
2649 
2651 {
2652  if(screen_.update_locked())
2653  return;
2654 
2655  invalidateGameStatus_ = true;
2656 
2657  reportRects_.clear();
2658 #ifdef SDL_GPU
2659  reportImages_.clear();
2660 #else
2661  reportSurfaces_.clear();
2662 #endif
2663  reports_.clear();
2664 
2666 
2668 
2670 
2671  if(!menu_buttons_.empty() || !action_buttons_.empty() || !sliders_.empty() ) {
2672  create_buttons();
2673  }
2674 
2675  panelsDrawn_ = false;
2676  if (!gui::in_dialog()) {
2678  }
2679 
2680  redraw_background_ = true;
2681 
2682  int ticks1 = SDL_GetTicks();
2683  invalidate_all();
2684  int ticks2 = SDL_GetTicks();
2685  draw(true,true);
2686  int ticks3 = SDL_GetTicks();
2687  LOG_DP << "invalidate and draw: " << (ticks3 - ticks2) << " and " << (ticks2 - ticks1) << "\n";
2688 
2689  for (std::function<void(display&)> f : redraw_observers_) {
2690  f(*this);
2691  }
2692 
2694 }
2695 
2696 void display::add_redraw_observer(std::function<void(display&)> f)
2697 {
2698  redraw_observers_.push_back(f);
2699 }
2700 
2702 {
2703  redraw_observers_.clear();
2704 }
2705 
2707  draw(true, false);
2708 }
2709 
2710 void display::draw(bool update) {
2711  draw(update, false);
2712 }
2713 
2714 
2715 void display::draw(bool update,bool force) {
2716 // log_scope("display::draw");
2717 
2718  if (screen_.update_locked()) {
2719  return;
2720  }
2721 
2722  if (dirty_) {
2723  dirty_ = false;
2725  return;
2726  }
2727 
2728  // Trigger cache rebuild when preference gets changed
2731  builder_->rebuild_cache_all();
2732  }
2733 
2735 
2736  draw_init();
2737  pre_draw();
2738  // invalidate all that needs to be invalidated
2740  // at this stage we have everything that needs to be invalidated for this redraw
2741  // save it as the previous invalidated, and merge with the previous invalidated_
2742  // we merge with the previous redraw because if a hex had a unit last redraw but
2743  // not this one, nobody will tell us to redraw (cleanup)
2746  // these new invalidations cannot cause any propagation because
2747  // if a hex was invalidated last turn but not this turn, then
2748  // * case of no unit in neighbor hex=> no propagation
2749  // * case of unit in hex but was there last turn=>its hexes are invalidated too
2750  // * case of unit in hex not there last turn => it moved, so was invalidated previously
2751  if(!get_map().empty()) {
2752  //int simulate_delay = 0;
2753 
2754  /*
2755  * draw_invalidated() also invalidates the halos, so also needs to be
2756  * ran if invalidated_.empty() == true.
2757  */
2758  if(!invalidated_.empty() || preferences::show_haloes()) {
2759  draw_invalidated();
2760  invalidated_.clear();
2761  }
2763  post_commit();
2764  draw_sidebar();
2765 
2766  // Simulate slow PC:
2767  //SDL_Delay(2*simulate_delay + rand() % 20);
2768  }
2769  draw_wrap(update, force);
2770  post_draw();
2771 }
2772 
2774 {
2775  return *map_labels_;
2776 }
2777 
2779 {
2780  return *map_labels_;
2781 }
2782 
2784 {
2785  surface& disp(screen_.getSurface());
2786  SDL_Rect area = screen_area();
2787  sdl::fill_rect(disp, &area, SDL_MapRGB(disp->format, 0, 0, 0));
2788 }
2789 
2790 const SDL_Rect& display::get_clip_rect()
2791 {
2792  return map_area();
2793 }
2794 
2796 // log_scope("display::draw_invalidated");
2797  SDL_Rect clip_rect = get_clip_rect();
2799  clip_rect_setter set_clip_rect(screen, &clip_rect);
2800  for (const map_location& loc : invalidated_) {
2801  int xpos = get_location_x(loc);
2802  int ypos = get_location_y(loc);
2803 
2804  update_rect(xpos, ypos, zoom_, zoom_);
2805 
2806  const bool on_map = get_map().on_board(loc);
2807  SDL_Rect hex_rect = sdl::create_rect(xpos, ypos, zoom_, zoom_);
2808  if(!sdl::rects_overlap(hex_rect,clip_rect)) {
2809  continue;
2810  }
2811  draw_hex(loc);
2812  drawn_hexes_+=1;
2813  // If the tile is at the border, we start to blend it
2814  if(!on_map) {
2815  draw_border(loc, xpos, ypos);
2816  }
2817  }
2818  invalidated_hexes_ += invalidated_.size();
2819 
2820  unit_drawer drawer = unit_drawer(*this, energy_bar_rects_);
2821 
2822  for (const map_location& loc : invalidated_) {
2823  unit_map::const_iterator u_it = dc_->units().find(loc);
2825  if (u_it != dc_->units().end()
2826  && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
2827  drawer.redraw_unit(*u_it);
2828  }
2829 
2830 }
2831 
2832 //TODO: proper SDL_gpu implementation
2834  int xpos = get_location_x(loc);
2835  int ypos = get_location_y(loc);
2836  image::TYPE image_type = get_image_type(loc);
2837  const bool on_map = get_map().on_board(loc);
2838  const bool off_map_tile = (get_map().get_terrain(loc) == t_translation::OFF_MAP_USER);
2839  const time_of_day& tod = get_time_of_day(loc);
2840  if(!shrouded(loc)) {
2841  // unshrouded terrain (the normal case)
2842  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos,
2843  get_terrain_images(loc,tod.id, image_type, BACKGROUND));
2844 
2845  drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
2846  get_terrain_images(loc,tod.id,image_type, FOREGROUND));
2847 
2848 #ifdef SDL_GPU
2849  // Draw the grid, if that's been enabled
2850  if(grid_ && on_map && !off_map_tile) {
2852  drawing_buffer_add(LAYER_GRID_TOP, loc, xpos, ypos,
2853  image::get_texture(grid_top, image::TOD_COLORED));
2855  drawing_buffer_add(LAYER_GRID_BOTTOM, loc, xpos, ypos,
2856  image::get_texture(grid_bottom, image::TOD_COLORED));
2857  }
2858  // village-control flags.
2859  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, get_flag(loc));
2860 #else
2861  // Draw the grid, if that's been enabled
2862  if(grid_ && on_map && !off_map_tile) {
2864  drawing_buffer_add(LAYER_GRID_TOP, loc, xpos, ypos,
2867  drawing_buffer_add(LAYER_GRID_BOTTOM, loc, xpos, ypos,
2868  image::get_image(grid_bottom, image::TOD_COLORED));
2869  }
2870  // village-control flags.
2871  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, get_flag(loc));
2872 #endif
2873  }
2874 
2875  if(!shrouded(loc)) {
2876  typedef overlay_map::const_iterator Itor;
2877  std::pair<Itor,Itor> overlays = overlays_->equal_range(loc);
2878  const bool have_overlays = overlays.first != overlays.second;
2879 
2881 
2882  if(have_overlays) {
2883  const time_of_day& loc_tod = get_time_of_day(loc);
2884  const tod_color& loc_col = loc_tod.color;
2885 
2886  if(loc_col != get_time_of_day().color) {
2887  // Continue with local light. image::get_lighted_image
2888  // doesn't take the image::TOD_COLORED type, so we need
2889  // to apply the color_adjust_ ourselves.
2890  tod_color col = loc_col + color_adjust_;
2891  lt = image::get_light_string(-1, col.r, col.g, col.b);
2892  }
2893  }
2894 
2895  for( ; overlays.first != overlays.second; ++overlays.first) {
2896  if ((overlays.first->second.team_name == "" ||
2897  overlays.first->second.team_name.find(dc_->teams()[viewing_team()].team_name()) != std::string::npos)
2898  && !(fogged(loc) && !overlays.first->second.visible_in_fog))
2899  {
2900 
2901 #ifdef SDL_GPU
2902  const sdl::timage img = use_local_light
2903  ? image::get_lighted_texture(overlays.first->second.image, lt, image::SCALED_TO_HEX)
2904  : image::get_texture(overlays.first->second.image, image_type);
2905  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, img);
2906 #else
2907  const surface surf =
2908  image::get_lighted_image(overlays.first->second.image, lt, image::SCALED_TO_HEX);
2909  drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos, surf);
2910 #endif
2911  }
2912  }
2913  }
2914 
2915  // Draw the time-of-day mask on top of the terrain in the hex.
2916  // tod may differ from tod if hex is illuminated.
2917  const std::string& tod_hex_mask = tod.image_mask;
2918 #ifdef SDL_GPU
2919  if(!tod_hex_mask1.null() || !tod_hex_mask2.null()) {
2922  } else if(!tod_hex_mask.empty()) {
2923  drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
2924  image::get_texture(tod_hex_mask,image::SCALED_TO_HEX));
2925  }
2926 #else
2927  if(tod_hex_mask1 != nullptr || tod_hex_mask2 != nullptr) {
2930  } else if(!tod_hex_mask.empty()) {
2931  drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
2932  image::get_image(tod_hex_mask,image::SCALED_TO_HEX));
2933  }
2934 #endif
2935 
2936  // Paint mouseover overlays
2937  if(loc == mouseoverHex_ && (on_map || (in_editor() && get_map().on_board_with_border(loc)))
2938 #ifdef SDL_GPU
2939  && !mouseover_hex_overlay_.null()) {
2940 #else
2941  && mouseover_hex_overlay_ != nullptr) {
2942 #endif
2944  }
2945 
2946  // Paint arrows
2947  arrows_map_t::const_iterator arrows_in_hex = arrows_map_.find(loc);
2948  if(arrows_in_hex != arrows_map_.end()) {
2949  for (arrow* const a : arrows_in_hex->second) {
2950  a->draw_hex(loc);
2951  }
2952  }
2953 
2954  // Apply shroud, fog and linger overlay
2955 
2956 #ifdef SDL_GPU
2957  if(shrouded(loc)) {
2958  // We apply void also on off-map tiles
2959  // to shroud the half-hexes too
2960  const std::string& shroud_image = get_variant(shroud_images_, loc);
2961  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2962  image::get_texture(shroud_image, image_type));
2963  } else if(fogged(loc)) {
2964  const std::string& fog_image = get_variant(fog_images_, loc);
2965  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2966  image::get_texture(fog_image, image_type));
2967  }
2968 #else
2969  if(shrouded(loc)) {
2970  // We apply void also on off-map tiles
2971  // to shroud the half-hexes too
2972  const std::string& shroud_image = get_variant(shroud_images_, loc);
2973  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2974  image::get_image(shroud_image, image_type));
2975  } else if(fogged(loc)) {
2976  const std::string& fog_image = get_variant(fog_images_, loc);
2977  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
2978  image::get_image(fog_image, image_type));
2979  }
2980 #endif
2981 
2982  if(!shrouded(loc)) {
2983  drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos, get_fog_shroud_images(loc, image_type));
2984  }
2985 
2986  if (on_map) {
2987  if (draw_coordinates_) {
2988  int off_x = xpos + hex_size()/2;
2989  int off_y = ypos + hex_size()/2;
2990  surface text = font::get_rendered_text(lexical_cast<std::string>(loc), font::SIZE_SMALL, font::NORMAL_COLOR);
2991  surface bg = create_neutral_surface(text->w, text->h);
2992  SDL_Rect bg_rect = sdl::create_rect(0, 0, text->w, text->h);
2993  sdl::fill_rect(bg, &bg_rect, 0xaa000000);
2994  off_x -= text->w / 2;
2995  if (draw_terrain_codes_) {
2996  off_y -= text->h;
2997  } else {
2998  off_y -= text->h / 2;
2999  }
3000 #ifdef SDL_GPU
3001  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, sdl::timage(bg));
3002  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, sdl::timage(text));
3003 #else
3004  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
3005  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
3006 #endif
3007  }
3008  if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) {
3009  int off_x = xpos + hex_size()/2;
3010  int off_y = ypos + hex_size()/2;
3011  surface text = font::get_rendered_text(lexical_cast<std::string>(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOR);
3012  surface bg = create_neutral_surface(text->w, text->h);
3013  SDL_Rect bg_rect = sdl::create_rect(0, 0, text->w, text->h);
3014  sdl::fill_rect(bg, &bg_rect, 0xaa000000);
3015  off_x -= text->w / 2;
3016  if (!draw_coordinates_) {
3017  off_y -= text->h / 2;
3018  }
3019 #ifdef SDL_GPU
3020  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, sdl::timage(bg));
3021  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, sdl::timage(text));
3022 #else
3023  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
3024  drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
3025 #endif
3026  }
3027  }
3028 
3029  if(debug_foreground) {
3030 #ifdef SDL_GPU
3031  drawing_buffer_add(LAYER_UNIT_DEFAULT, loc, xpos, ypos,
3032  image::get_texture("terrain/foreground.png", image_type));
3033 #else
3034  drawing_buffer_add(LAYER_UNIT_DEFAULT, loc, xpos, ypos,
3035  image::get_image("terrain/foreground.png", image_type));
3036 #endif
3037  }
3038 
3039 }
3040 
3042  return image::TOD_COLORED;
3043 }
3044 
3045 /*void display::draw_sidebar() {
3046 
3047 }*/
3048 
3049 #ifdef SDL_GPU
3050 void display::draw_image_for_report(sdl::timage& img, SDL_Rect& rect)
3051 {
3052  const float scale_factor = std::min(rect.w / img.base_width(), rect.h / img.base_height());
3053  img.set_scale(scale_factor, scale_factor);
3054  const int xpos = rect.x + (rect.w - img.width())/2;
3055  const int ypos = rect.y + (rect.h - img.height())/2;
3056 
3057  screen_.draw_texture(img, xpos, ypos);
3058 }
3059 #else
3060 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
3061 {
3062  SDL_Rect visible_area = get_non_transparent_portion(img);
3063  SDL_Rect target = rect;
3064  if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
3065  if(visible_area.w == 0 || visible_area.h == 0) {
3066  return;
3067  }
3068 
3069  if(visible_area.w > rect.w || visible_area.h > rect.h) {
3070  img.assign(get_surface_portion(img,visible_area));
3071  img.assign(scale_surface(img,rect.w,rect.h));
3072  visible_area.x = 0;
3073  visible_area.y = 0;
3074  visible_area.w = img->w;
3075  visible_area.h = img->h;
3076  } else {
3077  target.x = rect.x + (rect.w - visible_area.w)/2;
3078  target.y = rect.y + (rect.h - visible_area.h)/2;
3079  target.w = visible_area.w;
3080  target.h = visible_area.h;
3081  }
3082 
3083  sdl_blit(img,&visible_area,screen_.getSurface(),&target);
3084  } else {
3085  if(img->w != rect.w || img->h != rect.h) {
3086  img.assign(scale_surface(img,rect.w,rect.h));
3087  }
3088 
3089  sdl_blit(img,nullptr,screen_.getSurface(),&target);
3090  }
3091 }
3092 #endif
3093 
3094 /**
3095  * Redraws the specified report (if anything has changed).
3096  * If a config is not supplied, it will be generated via
3097  * reports::generate_report().
3098  */
3099 void display::refresh_report(std::string const &report_name, const config * new_cfg)
3100 {
3101 #ifdef SDL_GPU
3102  const theme::status_item *item = theme_.get_status_item(report_name);
3103  if (!item) {
3104  reportImages_[report_name] = sdl::timage();
3105  return;
3106  }
3107 
3108  // Now we will need the config. Generate one if needed.
3109 
3110  boost::optional <events::mouse_handler &> mhb = boost::none;
3111 
3112  if (resources::controller) {
3114  }
3115 
3116  reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
3117 
3118  const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
3119  if ( new_cfg == nullptr )
3120  new_cfg = &generated_cfg;
3121 
3122  SDL_Rect &rect = reportRects_[report_name];
3123  const SDL_Rect &new_rect = item->location(screen_area());
3124  sdl::timage &img = reportImages_[report_name];
3125  config &report = reports_[report_name];
3126 
3127  // Report and its location is unchanged since last time. Do nothing.
3128  if (!img.null() && rect == new_rect && report == *new_cfg) {
3129  return;
3130  }
3131 
3132  // Update the config in reports_.
3133  report = *new_cfg;
3134 
3135  if (!img.null()) {
3136  screen_.draw_texture(img, rect.x, rect.y);
3137  }
3138 
3139  // If the rectangle has just changed, assign the surface to it
3140  if (img.null() || new_rect != rect)
3141  {
3142  img = sdl::timage();
3143  rect = new_rect;
3144 
3145  // If the rectangle is present, and we are blitting text,
3146  // then we need to backup the surface.
3147  // (Images generally won't need backing up,
3148  // unless they are transparent, but that is done later).
3149  // TODO: handle this somehow for SDL_gpu
3150  /*if (rect.w > 0 && rect.h > 0) {
3151  img.assign(get_surface_portion(screen_.getSurface(), rect));
3152  if (reportSurfaces_[report_name] == nullptr) {
3153  ERR_DP << "Could not backup background for report!" << std::endl;
3154  }
3155  }
3156  update_rect(rect);*/
3157  }
3158 
3160  draw_panel_image(&rect);
3161 
3162  if (report.empty()) return;
3163 
3164  int x = rect.x, y = rect.y;
3165 
3166  // Add prefix, postfix elements.
3167  // Make sure that they get the same tooltip
3168  // as the guys around them.
3169  std::string str = item->prefix();
3170  if (!str.empty()) {
3171  config &e = report.add_child_at("element", config(), 0);
3172  e["text"] = str;
3173  e["tooltip"] = report.child("element")["tooltip"];
3174  }
3175  str = item->postfix();
3176  if (!str.empty()) {
3177  config &e = report.add_child("element");
3178  e["text"] = str;
3179  e["tooltip"] = report.child("element", -1)["tooltip"];
3180  }
3181 
3182  // Loop through and display each report element.
3183  int tallest = 0;
3184  int image_count = 0;
3185  bool used_ellipsis = false;
3186  std::ostringstream ellipsis_tooltip;
3187  SDL_Rect ellipsis_area = rect;
3188 
3189  for (config::const_child_itors elements = report.child_range("element");
3190  elements.first != elements.second; ++elements.first)
3191  {
3192  SDL_Rect area = sdl::create_rect(x, y, rect.w + rect.x - x, rect.h + rect.y - y);
3193  if (area.h <= 0) break;
3194 
3195  std::string t = (*elements.first)["text"];
3196  if (!t.empty())
3197  {
3198  if (used_ellipsis) goto skip_element;
3199 
3200  // Draw a text element.
3201  font::ttext text;
3202  if (item->font_rgb_set()) {
3203  // font_rgb() has no alpha channel and uses a 0x00RRGGBB
3204  // layout instead of 0xRRGGBBAA which is what ttext expects,
3205  // so shift the value to the left and add fully-opaque alpha.
3206  text.set_foreground_color((item->font_rgb() << 8) + 0xFF);
3207  }
3208  bool eol = false;
3209  if (t[t.size() - 1] == '\n') {
3210  eol = true;
3211  t = t.substr(0, t.size() - 1);
3212  }
3213  text.set_font_size(item->font_size());
3214  text.set_text(t, true);
3215  text.set_maximum_width(area.w);
3216  text.set_maximum_height(area.h, false);
3217  sdl::timage text_img = text.render_as_texture();
3218 
3219  // check if next element is text with almost no space to show it
3220  const int minimal_text = 12; // width in pixels
3221  config::const_child_iterator ee = elements.first;
3222  if (!eol && rect.w - (x - rect.x + text_img.width()) < minimal_text &&
3223  ++ee != elements.second && !(*ee)["text"].empty())
3224  {
3225  // make this element longer to trigger rendering of ellipsis
3226  // (to indicate that next elements have not enough space)
3227  //NOTE this space should be longer than minimal_text pixels
3228  t = t + " ";
3229  text.set_text(t, true);
3230  text_img = text.render_as_texture();
3231  // use the area of this element for next tooltips
3232  used_ellipsis = true;
3233  ellipsis_area.x = x;
3234  ellipsis_area.y = y;
3235  ellipsis_area.w = text_img.width();
3236  ellipsis_area.h = text_img.height();
3237  }
3238 
3239  screen_.draw_texture(text_img, x, y);
3240  area.w = text_img.width();
3241  area.h = text_img.height();
3242  if (area.h > tallest) {
3243  tallest = area.h;
3244  }
3245  if (eol) {
3246  x = rect.x;
3247  y += tallest;
3248  tallest = 0;
3249  } else {
3250  x += area.w;
3251  }
3252  }
3253  else if (!(t = (*elements.first)["image"].str()).empty())
3254  {
3255  if (used_ellipsis) goto skip_element;
3256 
3257  // Draw an image element.
3258  sdl::timage image(image::get_texture(t));
3259 
3260  if (image.null()) {
3261  ERR_DP << "could not find image for report: '" << t << "'" << std::endl;
3262  continue;
3263  }
3264 
3265  if (area.w < image.width() && image_count) {
3266  // We have more than one image, and this one doesn't fit.
3267  image = image::get_texture(game_config::images::ellipsis);
3268  used_ellipsis = true;
3269  }
3270 
3271  if (image.width() < area.w) area.w = image.width();
3272  if (image.height() < area.h) area.h = image.height();
3273  draw_image_for_report(image, area);
3274 
3275  ++image_count;
3276  if (area.h > tallest) {
3277  tallest = area.h;
3278  }
3279 
3280  if (!used_ellipsis) {
3281  x += area.w;
3282  } else {
3283  ellipsis_area = area;
3284  }
3285  }
3286  else
3287  {
3288  // No text nor image, skip this element
3289  continue;
3290  }
3291 
3292  skip_element:
3293  t = (*elements.first)["tooltip"].t_str().base_str();
3294  if (!t.empty()) {
3295  if (!used_ellipsis) {
3296  tooltips::add_tooltip(area, t, (*elements.first)["help"].t_str().base_str());
3297  } else {
3298  // Collect all tooltips for the ellipsis.
3299  // TODO: need a better separator
3300  // TODO: assign an action
3301  ellipsis_tooltip << t;
3302  config::const_child_iterator ee = elements.first;
3303  if (++ee != elements.second)
3304  ellipsis_tooltip << "\n _________\n\n";
3305  }
3306  }
3307  }
3308 
3309  if (used_ellipsis) {
3310  tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
3311  }
3312 #else
3313  const theme::status_item *item = theme_.get_status_item(report_name);
3314  if (!item) {
3315  reportSurfaces_[report_name].assign(nullptr);
3316  return;
3317  }
3318 
3319  // Now we will need the config. Generate one if needed.
3320 
3321  boost::optional <events::mouse_handler &> mhb = boost::none;
3322 
3323  if (resources::controller) {
3325  }
3326 
3327  reports::context temp_context = reports::context(*dc_, *this, *resources::tod_manager, wb_.lock(), mhb);
3328 
3329  const config generated_cfg = new_cfg ? config() : reports_object_->generate_report(report_name, temp_context);
3330  if ( new_cfg == nullptr )
3331  new_cfg = &generated_cfg;
3332 
3333  SDL_Rect &rect = reportRects_[report_name];
3334  const SDL_Rect &new_rect = item->location(screen_area());
3335  surface &surf = reportSurfaces_[report_name];
3336  config &report = reports_[report_name];
3337 
3338  // Report and its location is unchanged since last time. Do nothing.
3339  if (surf && rect == new_rect && report == *new_cfg) {
3340  return;
3341  }
3342 
3343  // Update the config in reports_.
3344  report = *new_cfg;
3345 
3346  if (surf) {
3347  sdl_blit(surf, nullptr, screen_.getSurface(), &rect);
3348  update_rect(rect);
3349  }
3350 
3351  // If the rectangle has just changed, assign the surface to it
3352  if (!surf || new_rect != rect)
3353  {
3354  surf.assign(nullptr);
3355  rect = new_rect;
3356 
3357  // If the rectangle is present, and we are blitting text,
3358  // then we need to backup the surface.
3359  // (Images generally won't need backing up,
3360  // unless they are transparent, but that is done later).
3361  if (rect.w > 0 && rect.h > 0) {
3363  if (reportSurfaces_[report_name] == nullptr) {
3364  ERR_DP << "Could not backup background for report!" << std::endl;
3365  }
3366  }
3367  update_rect(rect);
3368  }
3369 
3371 
3372  if (report.empty()) return;
3373 
3374  int x = rect.x, y = rect.y;
3375 
3376  // Add prefix, postfix elements.
3377  // Make sure that they get the same tooltip
3378  // as the guys around them.
3379  std::string str = item->prefix();
3380  if (!str.empty()) {
3381  config &e = report.add_child_at("element", config(), 0);
3382  e["text"] = str;
3383  e["tooltip"] = report.child("element")["tooltip"];
3384  }
3385  str = item->postfix();
3386  if (!str.empty()) {
3387  config &e = report.add_child("element");
3388  e["text"] = str;
3389  e["tooltip"] = report.child("element", -1)["tooltip"];
3390  }
3391 
3392  // Loop through and display each report element.
3393  int tallest = 0;
3394  int image_count = 0;
3395  bool used_ellipsis = false;
3396  std::ostringstream ellipsis_tooltip;
3397  SDL_Rect ellipsis_area = rect;
3398 
3399  for (config::const_child_itors elements = report.child_range("element");
3400  elements.first != elements.second; ++elements.first)
3401  {
3402  SDL_Rect area = sdl::create_rect(x, y, rect.w + rect.x - x, rect.h + rect.y - y);
3403  if (area.h <= 0) break;
3404 
3405  std::string t = (*elements.first)["text"];
3406  if (!t.empty())
3407  {
3408  if (used_ellipsis) goto skip_element;
3409 
3410  // Draw a text element.
3411  font::ttext text;
3412  if (item->font_rgb_set()) {
3413  // font_rgb() has no alpha channel and uses a 0x00RRGGBB
3414  // layout instead of 0xRRGGBBAA which is what ttext expects,
3415  // so shift the value to the left and add fully-opaque alpha.
3416  text.set_foreground_color((item->font_rgb() << 8) + 0xFF);
3417  }
3418  bool eol = false;
3419  if (t[t.size() - 1] == '\n') {
3420  eol = true;
3421  t = t.substr(0, t.size() - 1);
3422  }
3423  text.set_font_size(item->font_size());
3424  text.set_text(t, true);
3425  text.set_maximum_width(area.w);
3426  text.set_maximum_height(area.h, false);
3427  surface s = text.render();
3428 
3429  // check if next element is text with almost no space to show it
3430  const int minimal_text = 12; // width in pixels
3431  config::const_child_iterator ee = elements.first;
3432  if (!eol && rect.w - (x - rect.x + s->w) < minimal_text &&
3433  ++ee != elements.second && !(*ee)["text"].empty())
3434  {
3435  // make this element longer to trigger rendering of ellipsis
3436  // (to indicate that next elements have not enough space)
3437  //NOTE this space should be longer than minimal_text pixels
3438  t = t + " ";
3439  text.set_text(t, true);
3440  s = text.render();
3441  // use the area of this element for next tooltips
3442  used_ellipsis = true;
3443  ellipsis_area.x = x;
3444  ellipsis_area.y = y;
3445  ellipsis_area.w = s->w;
3446  ellipsis_area.h = s->h;
3447  }
3448 
3449  screen_.blit_surface(x, y, s);
3450  area.w = s->w;
3451  area.h = s->h;
3452  if (area.h > tallest) {
3453  tallest = area.h;
3454  }
3455  if (eol) {
3456  x = rect.x;
3457  y += tallest;
3458  tallest = 0;
3459  } else {
3460  x += area.w;
3461  }
3462  }
3463  else if (!(t = (*elements.first)["image"].str()).empty())
3464  {
3465  if (used_ellipsis) goto skip_element;
3466 
3467  // Draw an image element.
3469 
3470  if (!img) {
3471  ERR_DP << "could not find image for report: '" << t << "'" << std::endl;
3472  continue;
3473  }
3474 
3475  if (area.w < img->w && image_count) {
3476  // We have more than one image, and this one doesn't fit.
3478  used_ellipsis = true;
3479  }
3480 
3481  if (img->w < area.w) area.w = img->w;
3482  if (img->h < area.h) area.h = img->h;
3483  draw_image_for_report(img, area);
3484 
3485  ++image_count;
3486  if (area.h > tallest) {
3487  tallest = area.h;
3488  }
3489 
3490  if (!used_ellipsis) {
3491  x += area.w;
3492  } else {
3493  ellipsis_area = area;
3494  }
3495  }
3496  else
3497  {
3498  // No text nor image, skip this element
3499  continue;
3500  }
3501 
3502  skip_element:
3503  t = (*elements.first)["tooltip"].t_str().base_str();
3504  if (!t.empty()) {
3505  if (!used_ellipsis) {
3506  tooltips::add_tooltip(area, t, (*elements.first)["help"].t_str().base_str());
3507  } else {
3508  // Collect all tooltips for the ellipsis.
3509  // TODO: need a better separator
3510  // TODO: assign an action
3511  ellipsis_tooltip << t;
3512  config::const_child_iterator ee = elements.first;
3513  if (++ee != elements.second)
3514  ellipsis_tooltip << "\n _________\n\n";
3515  }
3516  }
3517  }
3518 
3519  if (used_ellipsis) {
3520  tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
3521  }
3522 #endif
3523 }
3524 
3526 {
3527  DBG_DP << "invalidate_all()\n";
3528  invalidateAll_ = true;
3529 #ifdef _OPENMP
3530 #pragma omp critical(invalidated_)
3531 #endif //_OPENMP
3532  invalidated_.clear();
3533  update_rect(map_area());
3534 }
3535 
3537 {
3538  if(invalidateAll_)
3539  return false;
3540 
3541  bool tmp;
3542 #ifdef _OPENMP
3543 #pragma omp critical(invalidated_)
3544 #endif //_OPENMP
3545  tmp = invalidated_.insert(loc).second;
3546  return tmp;
3547 }
3548 
3549 bool display::invalidate(const std::set<map_location>& locs)
3550 {
3551  if(invalidateAll_)
3552  return false;
3553  bool ret = false;
3554  for (const map_location& loc : locs) {
3555 #ifdef _OPENMP
3556 #pragma omp critical(invalidated_)
3557 #endif //_OPENMP
3558  ret = invalidated_.insert(loc).second || ret;
3559  }
3560  return ret;
3561 }
3562 
3563 bool display::propagate_invalidation(const std::set<map_location>& locs)
3564 {
3565  if(invalidateAll_)
3566  return false;
3567 
3568  if(locs.size()<=1)
3569  return false; // propagation never needed
3570 
3571  bool result = false;
3572 #ifdef _OPENMP
3573 #pragma omp critical(invalidated_)
3574 #endif //_OPENMP
3575  {
3576  // search the first hex invalidated (if any)
3577  std::set<map_location>::const_iterator i = locs.begin();
3578  for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
3579 
3580  if (i != locs.end()) {
3581 
3582  // propagate invalidation
3583  // 'i' is already in, but I suspect that splitting the range is bad
3584  // especially because locs are often adjacents
3585  size_t previous_size = invalidated_.size();
3586  invalidated_.insert(locs.begin(), locs.end());
3587  result = previous_size < invalidated_.size();
3588  }
3589  }
3590  return result;
3591 }
3592 
3594 {
3596 }
3597 
3598 bool display::invalidate_locations_in_rect(const SDL_Rect& rect)
3599 {
3600  if(invalidateAll_)
3601  return false;
3602 
3603  bool result = false;
3604  for (const map_location &loc : hexes_under_rect(rect)) {
3605  result |= invalidate(loc);
3606  }
3607  return result;
3608 }
3609 
3611  if (get_map().is_village(loc)) {
3612  const int owner = dc_->village_owner(loc);
3613  if (owner >= 0 && flags_[owner].need_update()
3614  && (!fogged(loc) || !dc_->teams()[currentTeam_].is_enemy(owner+1))) {
3615  invalidate(loc);
3616  }
3617  }
3618 }
3619 
3621 {
3624  if (animate_map_) {
3625  for (const map_location &loc : get_visible_hexes())
3626  {
3627  if (shrouded(loc)) continue;
3628  if (builder_->update_animation(loc)) {
3629  invalidate(loc);
3630  } else {
3632  }
3633  }
3634  }
3635 
3636 #ifndef _OPENMP
3637  for (const unit & u : dc_->units()) {
3638  u.anim_comp().refresh();
3639  }
3640  for (const unit* u : *fake_unit_man_) {
3641  u->anim_comp().refresh();
3642  }
3643 #else
3644  std::vector<const unit *> open_mp_list;
3645  for (const unit & u : dc_->units()) {
3646  open_mp_list.push_back(&u);
3647  }
3648  // Note that it is an important assumption of the
3649  // system that the fake units are added to the list
3650  // after the real units, so that e.g. whiteboard
3651  // planned moves are drawn over the real units.
3652  for (const unit* u : *fake_unit_man_) {
3653  open_mp_list.push_back(u);
3654  }
3655 
3656  // openMP can't iterate over size_t
3657  const int omp_iterations = open_mp_list.size();
3658  //#pragma omp parallel for shared(open_mp_list)
3659  //this loop must not be parallelized. refresh is not thread-safe,
3660  //for one, unit filters are not thread safe. this is because,
3661  //adding new "scoped" wml variables is not thread safe. lua itself
3662  //is not thread safe. when this loop was parallelized, assertion
3663  //failures were reported in windows openmp builds.
3664  for (int i = 0; i < omp_iterations; i++) {
3665  open_mp_list[i]->anim_comp().refresh();
3666  }
3667 #endif
3668 
3669 
3670  bool new_inval;
3671  do {
3672  new_inval = false;
3673 #ifndef _OPENMP
3674  for (const unit & u : dc_->units()) {
3675  new_inval |= u.anim_comp().invalidate(*this);
3676  }
3677  for (const unit* u : *fake_unit_man_) {
3678  new_inval |= u->anim_comp().invalidate(*this);
3679  }
3680 #else
3681  #pragma omp parallel for reduction(|:new_inval) shared(open_mp_list)
3682  for (int i = 0; i < omp_iterations; i++) {
3683  new_inval |= open_mp_list[i]->anim_comp().invalidate(*this);
3684  }
3685 #endif
3686  } while (new_inval);
3687 }
3688 
3690 {
3691  const arrow_path_t & arrow_path = arrow.get_path();
3692  for (const map_location& loc : arrow_path)
3693  {
3694  arrows_map_[loc].push_back(&arrow);
3695  }
3696 }
3697 
3699 {
3700  const arrow_path_t & arrow_path = arrow.get_path();
3701  for (const map_location& loc : arrow_path)
3702  {
3703  arrows_map_[loc].remove(&arrow);
3704  }
3705 }
3706 
3708 {
3709  const arrow_path_t & previous_path = arrow.get_previous_path();
3710  for (const map_location& loc : previous_path)
3711  {
3712  arrows_map_[loc].remove(&arrow);
3713  }
3714  const arrow_path_t & arrow_path = arrow.get_path();
3715  for (const map_location& loc : arrow_path)
3716  {
3717  arrows_map_[loc].push_back(&arrow);
3718  }
3719 }
3720 
3721 void display::write(config& cfg) const
3722 {
3723  cfg["view_locked"] = view_locked_;
3724  cfg["color_adjust_red"] = color_adjust_.r;
3725  cfg["color_adjust_green"] = color_adjust_.g;
3726  cfg["color_adjust_blue"] = color_adjust_.b;
3727 }
3728 
3729 void display::read(const config& cfg)
3730 {
3731  view_locked_ = cfg["view_locked"].to_bool(false);
3732  color_adjust_.r = cfg["color_adjust_red"].to_int(0);
3733  color_adjust_.g = cfg["color_adjust_green"].to_int(0);
3734  color_adjust_.b = cfg["color_adjust_blue"].to_int(0);
3735 }
3736 
3738 {
3739  if (!reach_map_changed_) return;
3740  if (reach_map_.empty() != reach_map_old_.empty()) {
3741  // Invalidate everything except the non-darkened tiles
3742  reach_map &full = reach_map_.empty() ? reach_map_old_ : reach_map_;
3743 
3744  rect_of_hexes hexes = get_visible_hexes();
3745  rect_of_hexes::iterator i = hexes.begin(), end = hexes.end();
3746  for (;i != end; ++i) {
3747  reach_map::iterator reach = full.find(*i);
3748  if (reach == full.end()) {
3749  // Location needs to be darkened or brightened
3750  invalidate(*i);
3751  } else if (reach->second != 1) {
3752  // Number needs to be displayed or cleared
3753  invalidate(*i);
3754  }
3755  }
3756  } else if (!reach_map_.empty()) {
3757  // Invalidate only changes
3758  reach_map::iterator reach, reach_old;
3759  for (reach = reach_map_.begin(); reach != reach_map_.end(); ++reach) {
3760  reach_old = reach_map_old_.find(reach->first);
3761  if (reach_old == reach_map_old_.end()) {
3762  invalidate(reach->first);
3763  } else {
3764  if (reach_old->second != reach->second) {
3765  invalidate(reach->first);
3766  }
3767  reach_map_old_.erase(reach_old);
3768  }
3769  }
3770  for (reach_old = reach_map_old_.begin(); reach_old != reach_map_old_.end(); ++reach_old) {
3771  invalidate(reach_old->first);
3772  }
3773  }
3775  reach_map_changed_ = false;
3776 }
3777 
3778 void display::handle_window_event(const SDL_Event& event) {
3779  if (event.type == SDL_WINDOWEVENT) {
3780  switch (event.window.event) {
3781  case SDL_WINDOWEVENT_RESIZED:
3782  case SDL_WINDOWEVENT_RESTORED:
3783  case SDL_WINDOWEVENT_EXPOSED:
3784  dirty_ = true;
3785 
3786  break;
3787  }
3788  }
3789 
3790 
3791 }
3792 
3793 void display::handle_event(const SDL_Event& event) {
3795  return;
3796  }
3797  if (event.type == DRAW_ALL_EVENT) {
3798  draw();
3799  }
3800 }
3801 
3802 display *display::singleton_ = nullptr;
3803 
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:826
play_controller * controller
Definition: resources.cpp:21
TYPE
UNSCALED : image will be drawn "as is" without changing size, even in case of redraw SCALED_TO_ZOOM :...
Definition: image.hpp:208
void drawing_buffer_add(const tdrawing_layer layer, const map_location &loc, int x, int y, const surface &surf, const SDL_Rect &clip=SDL_Rect())
Add an item to the drawing buffer.
Definition: display.cpp:1241
static void sunset(const size_t delay=0)
Debug function to toggle the "sunset" mode.
Definition: display.cpp:1395
void drawing_buffer_commit()
Draws the drawing_buffer_ and clears it.
Definition: display.cpp:1324
boost::scoped_ptr< fake_unit_manager > fake_unit_man_
Definition: display.hpp:777
void draw_minimap_units()
Definition: display.cpp:2149
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void GPU_UnsetClip(GPU_Target *target)
Definition: SDL_gpu.c:1833
void raise_volatile_undraw_event()
Definition: events.cpp:613
SDL_Rect intersect_rects(SDL_Rect const &rect1, SDL_Rect const &rect2)
Calculates the intersection of two rectangles.
Definition: rect.cpp:58
std::string unmoved_color()
void recalculate_shroud()
Definition: label.cpp:285
Don't draw to this layer it's a dummy to size the vector.
Definition: display.hpp:906
std::set< map_location > previous_invalidated_
Definition: display.hpp:819
events::mouse_handler & get_mouse_handler_base()
Get a reference to a mouse handler member a derived class uses.
virtual void pre_draw()
Called near the beginning of each draw() call.
Definition: display.hpp:680
const std::string & id() const
Definition: unit.hpp:148
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.hpp:353
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:43
const std::string & text() const
Definition: theme.hpp:119
GLboolean enable
Definition: glew.h:2589
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:150
bool null() const
Definition: utils.hpp:104
virtual void select_hex(map_location hex)
Definition: display.cpp:1792
::tod_manager * tod_manager
Definition: resources.cpp:31
Arrows destined to be drawn on the map.
static const unsigned int max_layer_group
Definition: display.hpp:994
size_t currentTeam_
Definition: display.hpp:769
reports * reports_object_
Definition: display.hpp:791
Used for the bottom half part of grid image.
Definition: display.hpp:880
unit_iterator end()
Definition: map.hpp:311
bool minimap_draw_units()
virtual void handle_window_event(const SDL_Event &event)
Definition: display.cpp:3778
std::string border_image_bottom_odd
Definition: theme.hpp:104
bool show_fps()
std::string border_image_top_even
Definition: theme.hpp:102
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:299
Small struct to store and manipulate ToD colors.
Definition: time_of_day.hpp:30
std::shared_ptr< gui::zoom_slider > find_slider(const std::string &id)
Definition: display.cpp:846
virtual bool in_editor() const
Definition: display.hpp:208
const map_location pixel_position_to_hex(int x, int y) const
given x,y co-ordinates of a pixel on the map, will return the location of the hex that this pixel cor...
Definition: display.cpp:592
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glew.h:1222
void adjust_color_overlay(int r, int g, int b)
Add r,g,b to the colors for all images displayed on the map.
Definition: display.cpp:452
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:83
void fill_rect(surface &dst, SDL_Rect *dst_rect, const Uint32 color)
Fill a rectangle on a given surface.
Definition: rect.hpp:143
void invalidate_animations()
Function to invalidate animated terrains and units which may have changed.
Definition: display.cpp:3620
#define ERR_DP
Definition: display.cpp:62
virtual void draw_sidebar()
Called near the end of a draw operation, derived classes can use this to render a specific sidebar...
Definition: display.hpp:723
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3536
iterator end() const
Definition: display.cpp:661
void draw_wrap(bool update, bool force)
Definition: display.cpp:1869
std::string border_image_bottom_even
Definition: theme.hpp:105
void flip()
Definition: display.cpp:1411
Definition: unit.hpp:95
boost::weak_ptr< wb::manager > wb_
Definition: display.hpp:665
bool animate_water_
Local version of preferences::animate_water, used to detect when it's changed.
Definition: display.hpp:842
virtual void notify_observers()
GLint level
Definition: glew.h:1220
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
Definition: time_of_day.hpp:89
std::map< surface, SDL_Rect > energy_bar_rects_
Definition: display.hpp:771
const std::string & get_variant(const std::vector< std::string > &variants, const map_location &loc) const
Definition: display.cpp:476
std::string border_image_top_odd
Definition: theme.hpp:101
static void toggle_benchmark()
Toggle to continuously redraw the screen.
Definition: display.cpp:1401
std::string corner_image_top_left
Definition: theme.hpp:89
CKey keys_
Definition: display.hpp:836
void set_clip_rect(const SDL_Rect &r)
void change_display_context(const display_context *dc)
Definition: display.cpp:493
const SDL_Rect & minimap_area() const
Definition: display.hpp:222
Uint32 font_rgb() const
Definition: theme.hpp:126
const SDL_Color BLACK_COLOR
Definition: font.cpp:569
bool rects_overlap(const SDL_Rect &rect1, const SDL_Rect &rect2)
Tests whether two rectangles overlap.
Definition: rect.cpp:52
int ypos_
Definition: display.hpp:772
void set_team_colors(const std::vector< std::string > *colors)
set the team colors used by the TC image modification use a vector with one string for each team usin...
Definition: image.cpp:722
bool reach_map_changed_
Definition: display.hpp:1131
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
Uint32 rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:88
surface reverse_image(const surface &surf)
function to reverse an image.
Definition: image.cpp:1165
void draw_init()
Initiate a redraw.
Definition: display.cpp:1825
void set_default_zoom()
Sets the zoom amount to the default.
Definition: display.cpp:2326
void set_idle_anim_rate(int rate)
Definition: display.cpp:2645
Sint32 fixed_t
Definition: drawer.hpp:40
void scroll_floating_labels(double xmove, double ymove)
moves all floating labels that have 'scroll_mode' set to ANCHOR_LABEL_MAP
SDL_Color int_to_color(const Uint32 rgb)
Definition: utils.cpp:63
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:274
int draw_delay()
Definition: video.hpp:58
arrow_path_t const & get_path() const
Definition: arrow.cpp:123
bool minimap_movement_coding()
std::map< std::string, SDL_Rect > reportRects_
Definition: display.hpp:809
game_display * screen
Definition: resources.cpp:27
static int report(lua_State *L, int status)
Definition: lua.cpp:134
void draw_all_panels()
redraw all panels associated with the map display
Definition: display.cpp:1568
bool in_dialog()
Definition: show_dialog.cpp:57
virtual void draw_invalidated()
Only called when there's actual redrawing to do.
Definition: display.cpp:2795
const SDL_Rect empty_rect
Definition: rect.cpp:26
map_location mouseoverHex_
Definition: display.hpp:835
void flip()
Definition: video.cpp:496
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition: display.cpp:265
Manages a list of fake units for the display object.
static void draw_background(surface screen, const SDL_Rect &area, const std::string &image)
Definition: display.cpp:1647
std::string id
Definition: time_of_day.hpp:77
void lock_updates(bool value)
Definition: video.cpp:509
bool draw_coordinates_
Debug flag - overlay x,y coords on tiles.
Definition: display.hpp:1155
int scroll_speed()
void remove_floating_label(int handle)
removes the floating label given by 'handle' from the screen
void redraw_everything()
Invalidates entire screen, including all tiles and sidebar.
Definition: display.cpp:2650
static lg::log_domain log_display("display")
int fps_handle_
Handle for the label which displays frames per second.
Definition: display.hpp:1142
GLenum GLsizei GLenum GLenum const GLvoid * image
Definition: glew.h:3783
bool is_odd(T num)
Definition: util.hpp:37
The class terrain_builder is constructed from a config object, and a gamemap object.
Definition: builder.hpp:43
map_location minimap_location_on(int x, int y)
given x,y co-ordinates of the mouse, will return the location of the hex in the minimap that the mous...
Definition: display.cpp:723
Layer for the terrain drawn behind the unit.
Definition: display.hpp:865
bool idle_anim()
void reset_halo_manager()
Definition: display.cpp:499
unit_iterator begin()
Definition: map.hpp:308
void add_frame(int duration, const T &value, bool force_change=false)
Adds a frame to an animation.
virtual void draw_border(const map_location &loc, const int xpos, const int ypos)
Draws the border tile overlay.
Definition: display.cpp:1963
bool tile_nearly_on_screen(const map_location &loc) const
Checks if location loc or one of the adjacent tiles is visible on screen.
Definition: display.cpp:2345
const map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:577
ttext & set_font_size(const unsigned font_size)
Definition: text.cpp:406
const std::vector< menu > & menus() const
Definition: theme.hpp:263
const std::string & image() const
Definition: theme.hpp:168
reach_map reach_map_
Definition: display.hpp:1129
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
const rect_of_hexes & rect_
Definition: display.hpp:334
double get_zoom_factor() const
Returns the current zoom factor.
Definition: display.hpp:269
iterator & operator++()
increment y first, then when reaching bottom, increment x
Definition: display.cpp:644
std::vector< std::function< void(display &)> > redraw_observers_
Definition: display.hpp:1152
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:713
bool tile_fully_on_screen(const map_location &loc)
Check if a tile is fully visible on screen.
Definition: display.cpp:2338
std::vector< surface > get_terrain_images(const map_location &loc, const std::string &timeid, image::TYPE type, TERRAIN_TYPE terrain_type)
Definition: display.cpp:1126
GLboolean GLboolean g
Definition: glew.h:7319
std::string border_image_right
Definition: theme.hpp:99
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
map_location selectedHex_
Definition: display.hpp:834
void set_font_size(int font_size)
void draw_text_in_hex(const map_location &loc, const tdrawing_layer layer, const std::string &text, size_t font_size, SDL_Color color, double x_in_hex=0.5, double y_in_hex=0.5)
Draw text on a hex.
Definition: display.cpp:1668
std::string corner_image_bottom_right_even
Definition: theme.hpp:96
const std::vector< std::string > items
CVideo & screen_
Definition: display.hpp:768
surface scale_surface(const surface &surf, int w, int h)
Definition: utils.cpp:443
static display * singleton_
Definition: display.hpp:1178
ttext & set_maximum_width(int width)
Definition: text.cpp:446
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Definition: video.cpp:290
#define d
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2706
bool animate_water()
const int SIZE_PLUS
Definition: font.hpp:65
const config & child_or_empty(const std::string &key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:722
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2335
void enable_menu(const std::string &item, bool enable)
Finds the menu which has a given item in it, and enables or disables it.
Definition: display.cpp:1932
Rectangular area of hexes, allowing to decide how the top and bottom edges handles the vertical shift...
Definition: display.hpp:309
an object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
-file sdl_utils.hpp
bool empty() const
Definition: config.cpp:1105
void clear_redraw_observers()
Clear the redraw observers.
Definition: display.cpp:2701
bool font_rgb_set() const
Definition: theme.hpp:127
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
GLdouble GLdouble t
Definition: glew.h:1366
const t_terrain OFF_MAP_USER
virtual const gamemap & map() const =0
std::vector< surface > get_fog_shroud_images(const map_location &loc, image::TYPE image_type)
Definition: display.cpp:1019
int round_double(double d)
Definition: util.hpp:67
void remove_arrow(arrow &)
Definition: display.cpp:3698
surface & getSurface()
Definition: dummy_video.cpp:22
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
bool panelsDrawn_
Definition: display.hpp:786
#define DBG_DP
Definition: display.cpp:64
surface tod_hex_mask2
Definition: display.hpp:829
SDL_Rect get_non_transparent_portion(const surface &surf)
Definition: utils.cpp:2381
expression_ptr key_
Definition: formula.cpp:435
Layer for the terrain drawn in front of the unit.
Definition: display.hpp:876
Top half part of grid image.
Definition: display.hpp:869
void update_display()
Copy the backbuffer to the framebuffer.
Definition: display.cpp:1446
std::vector< map_location > arrow_path_t
Definition: arrow.hpp:25
SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:342
Unit and team statistics.
Mouseover overlay used by editor.
Definition: display.hpp:870
int get_location_y(const map_location &loc) const
Definition: display.cpp:718
very simple iterator to walk into the rect_of_hexes
Definition: display.hpp:316
virtual const tod_manager & get_tod_man() const
This is implemented properly in game_display. The display:: impl could be pure virtual here but we de...
Definition: display.cpp:440
bool save_image(const locator &i_locator, const std::string &filename)
Definition: image.cpp:1249
"black stripes" on unreachable hexes.
Definition: display.hpp:891
std::vector< std::shared_ptr< gui::button > > action_buttons_
Definition: display.hpp:816
surface map_screenshot_surf_
Definition: display.hpp:1150
static const tdrawing_layer layer_groups[]
Definition: display.hpp:993
void clear_tooltips()
Definition: tooltips.cpp:125
SDL_Rect screen_area()
Definition: video.cpp:135
const theme::action * action_pressed()
Definition: display.cpp:1899
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2185
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
const int SIZE_XLARGE
Definition: font.hpp:68
GLintptr offset
Definition: glew.h:1650
std::vector< std::string > shroud_images_
Definition: display.hpp:832
void draw_image_for_report(surface &img, SDL_Rect &rect)
Definition: display.cpp:3060
GLuint GLuint stream
Definition: glew.h:5239
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:48
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: image.cpp:1187
void blindfold(bool flag)
Definition: display.cpp:509
static std::vector< team > *& teams
Definition: team.cpp:50
tdrawing_layer
The layers to render something on.
Definition: display.hpp:864
const std::vector< label > & labels() const
Definition: theme.hpp:262
events::generic_event complete_redraw_event_
notify observers that the screen has been redrawn completely atm this is used for replay_controller t...
Definition: display.hpp:800
void parse_team_overlays()
Check the overlay_map for proper team-specific overlays to be displayed/hidden.
Definition: display.cpp:81
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
Uint32 font_rgb() const
Definition: theme.hpp:150
bool font_rgb_set() const
Definition: theme.hpp:151
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
GLclampf GLclampf blue
Definition: glew.h:1488
void draw_minimap()
Definition: display.cpp:2088
GLuint id
Definition: glew.h:1647
light_string get_light_string(int op, int r, int g, int b)
return light_string of one light operation(see above)
Definition: image.cpp:573
size_t playing_team() const
The playing team is the team whose turn it is.
Definition: display.hpp:97
const theme::menu * menu_pressed()
Definition: display.cpp:1916
void draw_centered_on_background(surface surf, const SDL_Rect &rect, const SDL_Color &color, surface target)
Definition: utils.cpp:2518
GLenum GLenum GLuint GLint GLint layer
Definition: glew.h:3455
const SDL_Color BAD_COLOR
Definition: font.cpp:568
Arrows destined to be drawn on the map.
Definition: arrow.hpp:30
bool valid() const
Definition: location.hpp:69
GLclampf green
Definition: glew.h:1488
void set_lifetime(int lifetime)
ttext & set_maximum_height(int height, bool multiline)
Definition: text.cpp:491
bool invalidate_visible_locations_in_rect(const SDL_Rect &rect)
Definition: display.cpp:3593
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:623
void scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force=true)
Definition: display.cpp:2355
const status_item * get_status_item(const std::string &item) const
Definition: theme.cpp:900
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
const std::string & postfix() const
Definition: theme.hpp:144
const GLdouble * v
Definition: glew.h:1359
GLsizei const GLfloat * value
Definition: glew.h:1817
static Uint8 red(Uint32 color)
Definition: display.hpp:178
surface adjust_surface_alpha(const surface &surf, fixed_t amount, bool optimize)
Definition: utils.cpp:1202
bool map_screenshot_
Used to indicate to drawing functions that we are doing a map screenshot.
Definition: display.hpp:1114
const std::string & get_id() const
Definition: theme.hpp:49
int w() const
Effective map width.
Definition: map.hpp:105
arrows_map_t arrows_map_
Maps the list of arrows for each location.
Definition: display.hpp:1162
virtual image::TYPE get_image_type(const map_location &loc)
Definition: display.cpp:3041
int xpos_
Definition: display.hpp:772
GLuint start
Definition: glew.h:1221
config generate_report(const std::string &name, context &ct, bool only_static=false)
Definition: reports.cpp:1569
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
surface blend_surface(const surface &surf, const double amount, const Uint32 color, const bool optimize)
Blends a surface with a color.
Definition: utils.cpp:1840
std::string corner_image_top_right_even
Definition: theme.hpp:93
const std::string & icon() const
Definition: theme.hpp:121
void render_buttons()
Definition: display.cpp:984
bool dirty_
Definition: display.hpp:1166
SDL_Rect minimap_location_
Definition: display.hpp:780
void set_color(const SDL_Color &color)
void render_image(int x, int y, const display::tdrawing_layer drawing_layer, const map_location &loc, surface image, bool hreverse=false, bool greyscale=false, fixed_t alpha=ftofxp(1.0), Uint32 blendto=0, double blend_ratio=0, double submerged=0.0, bool vreverse=false)
Draw an image at a certain location.
Definition: display.cpp:1724
int w() const
the dimensions of the display.
Definition: display.hpp:220
const SDL_Rect & max_map_area() const
Returns the maximum area used for the map regardless to resolution and view size. ...
Definition: display.cpp:523
bool empty() const
Tell if the map is of 0 size.
Definition: map.hpp:157
TERRAIN_TYPE
Used as a parameter for the get_terrain_at function.
Definition: builder.hpp:47
std::string grid_bottom
Definition: game_config.cpp:84
config & add_child(const std::string &key)
Definition: config.cpp:743
const std::string & team_name() const
Definition: team.hpp:297
const menu * get_menu_item(const std::string &key) const
Definition: theme.cpp:945
GLclampf GLclampf GLclampf alpha
Definition: glew.h:1488
tod_color color_adjust_
Definition: display.hpp:1164
static bool displaying()
Definition: loadscreen.hpp:49
std::string corner_image_bottom_left
Definition: theme.hpp:90
fake_unit_manager * fake_units
Definition: resources.cpp:32
default layer for drawing moving units
Definition: display.hpp:884
void bounds_check_position()
Definition: display.cpp:2587
virtual void post_draw()
Called at the very end of each draw() call.
Definition: display.hpp:687
Modify, read and display user preferences.
bool invalidateGameStatus_
Definition: display.hpp:789
void set_position(double xpos, double ypos)
void update_arrow(arrow &a)
Called by arrow objects when they change.
Definition: display.cpp:3707
map_display and display: classes which take care of displaying the map and game-data on the screen...
bool zoom_at_min() const
Definition: display.cpp:2280
virtual const unit_map & units() const =0
int h() const
height
Definition: display.hpp:221
size_t font_size() const
Definition: theme.hpp:149
bool animate_map()
int nextDraw_
Holds the tick count for when the next drawing event is scheduled.
Definition: display.hpp:806
static const std::string & get_direction(size_t n)
Definition: display.cpp:1010
std::string shroud_prefix
void create_buttons()
Definition: display.cpp:897
virtual const time_of_day & get_time_of_day(const map_location &loc=map_location::null_location()) const
Definition: display.cpp:430
int zoom_
Definition: display.hpp:775
GLfloat GLfloat p
Definition: glew.h:12766
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3525
void layout_buttons()
Definition: display.cpp:856
bool is_blindfolded() const
Definition: display.cpp:517
int invalidated_hexes_
Count work done for the debug info displayed under fps.
Definition: display.hpp:1144
bool draw_terrain_codes_
Debug flag - overlay terrain codes on tiles.
Definition: display.hpp:1157
GLuint const GLuint * names
Definition: glew.h:2552
surf
Definition: filter.cpp:143
const std::string eol
end of line + possible various character before.
GPU_Rect GPU_SetClip(GPU_Target *target, Sint16 x, Sint16 y, Uint16 w, Uint16 h)
Definition: SDL_gpu.c:1822
GLuint color
Definition: glew.h:5801
tdrawing_buffer drawing_buffer_
Definition: display.hpp:1060
void pump()
Definition: events.cpp:336
surface flop_surface(const surface &surf, bool optimize)
Definition: utils.cpp:2137
const std::string &parameters float amount
Definition: filter.cpp:132
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:47
std::string flag_rgb
bool non_interactive()
Definition: video.cpp:115
void set_color_adjustment(int r, int g, int b)
will make all scaled images have these rgb values added to all their pixels.
Definition: image.cpp:698
arrow_path_t const & get_previous_path() const
Definition: arrow.cpp:128
int idle_anim_rate()
void write(config &cfg) const
Definition: display.cpp:3721
Encapsulates the map of the game.
Definition: location.hpp:38
void set_team(const team *)
Definition: label.cpp:135
surface submerge_alpha(const surface &surf, int depth, float alpha_base, float alpha_delta, bool optimize)
Progressively reduce alpha of bottom part of the surface.
Definition: utils.cpp:1395
#define DRAW_ALL_EVENT
Definition: events.hpp:29
const std::string & get_filename() const
Definition: image.hpp:86
int get_end_time() const
double turbo_speed() const
Definition: display.cpp:2631
Reserve layers to be selected for WML.
Definition: display.hpp:873
std::string background_image
Definition: theme.hpp:86
boost::scoped_ptr< terrain_builder > builder_
Definition: display.hpp:778
default layer for drawing units
Definition: display.hpp:875
boost::scoped_ptr< map_labels > map_labels_
Definition: display.hpp:790
void process_reachmap_changes()
Definition: display.cpp:3737
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const SDL_Color &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
Definition: video.cpp:71
void recalculate_labels()
Definition: label.cpp:255
GLuint res
Definition: glew.h:9258
virtual void handle_event(const SDL_Event &)
Definition: display.cpp:3793
bool is_zero() const
Definition: time_of_day.hpp:33
std::string corner_image_bottom_right_odd
Definition: theme.hpp:95
surface & get_screen_surface()
return the screen surface or the surface used for map_screenshot.
Definition: display.hpp:205
surface tod_hex_mask1
Definition: display.hpp:829
void set_playing_team(size_t team)
set_playing_team sets the team whose turn it currently is
Definition: display.cpp:398
SDL_Rect screen_area() const
Definition: display.hpp:229
std::string allied_color()
virtual const std::vector< team > & teams() const =0
void new_animation_frame()
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
GLint left
Definition: glew.h:5907
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: glew.h:1222
surface render() const
Returns the rendered text.
Definition: text.cpp:166
static int last_zoom_
Definition: display.hpp:776
int h() const
Effective map height.
Definition: map.hpp:108
Fog and shroud.
Definition: display.hpp:893
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2434
int hex_width() const
Function which returns the width of a hex in pixels, up to where the next hex starts.
Definition: display.hpp:260
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1800
const std::vector< action > & actions() const
Definition: theme.hpp:265
bool update_locked() const
Definition: video.cpp:517
std::vector< std::shared_ptr< gui::button > > menu_buttons_
Definition: display.hpp:816
std::string moved_color()
void set_diagnostic(const std::string &msg)
Definition: display.cpp:1807
const std::vector< slider > & sliders() const
Definition: theme.hpp:264
void scroll_to_tiles(map_location loc1, map_location loc2, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, double add_spacing=0.0, bool force=true)
Scroll such that location loc1 is on-screen.
Definition: display.cpp:2446
void invalidate_animations_location(const map_location &loc)
Per-location invalidation called by invalidate_animations() Extra game per-location invalidation (vil...
Definition: display.cpp:3610
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.hpp:349
static SDL_Color get_minimap_color(int side)
Definition: team.cpp:833
GLuint index
Definition: glew.h:1782
drawing_buffer_key(const map_location &loc, tdrawing_layer layer)
Definition: display.cpp:1296
virtual ~display()
Definition: display.cpp:258
bool redraw_background_
Definition: display.hpp:782
void read(const config &cfg)
Definition: display.cpp:3729
bool dont_show_all_
Definition: display.hpp:770
std::basic_string< signed char > light_string
light_string store colors info of central and adjacent hexes.
Definition: image.hpp:149
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
void undraw_floating_labels(surface screen)
void init_flags_for_side_internal(size_t side, const std::string &side_color)
Definition: display.cpp:292
size_t i
Definition: function.cpp:1057
void raise_volatile_draw_event()
Definition: events.cpp:589
display(const display_context *dc, CVideo &video, boost::weak_ptr< wb::manager > wb, reports &reports_object, const config &theme_cfg, const config &level, bool auto_join=true)
Definition: display.cpp:147
surface brighten_image(const surface &surf, fixed_t amount, bool optimize)
Definition: utils.cpp:1160
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
bool invalidate_locations_in_rect(const SDL_Rect &rect)
invalidate all hexes under the rectangle rect (in screen coordinates)
Definition: display.cpp:3598
bool add_exclusive_draw(const map_location &loc, unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition: display.cpp:405
virtual void draw_hex(const map_location &loc)
Redraws a single gamemap location.
Definition: display.cpp:2833
Represents terrains which are to be drawn in front of them.
Definition: builder.hpp:52
void set_zoom(int amount)
sets the amount scaled images should be scaled.
Definition: image.cpp:736
theme theme_
Definition: display.hpp:774
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
virtual const SDL_Rect & get_clip_rect()
Get the clipping rectangle for drawing.
Definition: display.cpp:2790
bool view_locked_
Definition: display.hpp:773
double size
Definition: theme.hpp:84
std::string corner_image_top_right_odd
Definition: theme.hpp:92
surface mouseover_hex_overlay_
Definition: display.hpp:826
Definitions for the terrain builder.
const tborder & border() const
Definition: theme.hpp:292
Represents terrains which are to be drawn behind unit sprites.
Definition: builder.hpp:48
std::string tile_image
Definition: theme.hpp:87
Helper structure for rendering the terrains.
Definition: display.hpp:1003
const rect_of_hexes hexes_under_rect(const SDL_Rect &r) const
Return the rectangular area of hexes overlapped by r (r is in screen coordinates) ...
Definition: display.cpp:666
const std::vector< panel > & panels() const
Definition: theme.hpp:261
std::vector< std::string > fog_images_
Definition: display.hpp:831
const rect_of_hexes get_visible_hexes() const
Returns the rectangular area of visible hexes.
Definition: display.hpp:346
std::vector< animated< image::locator > > imagelist
A shorthand typedef for a list of animated image locators, the base data type returned by the get_ter...
Definition: builder.hpp:70
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
GLuint const GLchar * name
Definition: glew.h:1782
surface & get_video_surface()
Definition: dummy_video.cpp:36
t_translation::t_terrain get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:341
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: glew.h:1222
surface get_lighted_image(const image::locator &i_locator, const light_string &ls, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:1063
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
std::map< map_location, unsigned int > reach_map
Definition: display.hpp:1128
GLsizeiptr size
Definition: glew.h:1649
bool redrawMinimap_
Definition: display.hpp:781
void add_overlay(const map_location &loc, const std::string &image, const std::string &halo="", const std::string &team_name="", const std::string &item_id="", bool visible_under_fog=true)
Functions to add and remove overlays from locations.
Definition: display.cpp:97
overlay_map * overlays_
Definition: display.hpp:1139
bool set_resolution(const SDL_Rect &screen)
Definition: theme.cpp:633
bool faked() const
Definition: video.hpp:166
size_t activeTeam_
Definition: display.hpp:959
std::string partial_color()
std::string border_image_left
Definition: theme.hpp:98
Definition: display.hpp:43
virtual void draw_hex(map_location const &hex)
Definition: arrow.cpp:139
reach_map reach_map_old_
Definition: display.hpp:1130
static std::map< std::string, std::string > images
Definition: about.cpp:58
const SDL_Rect & map_area() const
Returns the area used for the map.
Definition: display.cpp:540
virtual void post_commit()
Hook for actions to take right after draw() calls drawing_buffer_commit No action here by default...
Definition: display.hpp:707
exclusive_unit_draw_requests_t exclusive_unit_draw_requests_
map of hexes where only one unit should be drawn, the one identified by the associated id string ...
Definition: display.hpp:669
GLclampd n
Definition: glew.h:5903
bool invalidateAll_
Definition: display.hpp:783
events::generic_event scroll_event_
Event raised when the map is being scrolled.
Definition: display.hpp:794
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:487
void fill_rect_alpha(SDL_Rect &rect, Uint32 color, Uint8 alpha, surface target)
Fills a specified area of a surface with a given color and opacity.
Definition: rect.cpp:83
const gamemap & get_map() const
Definition: display.hpp:92
#define g
Definition: glew.h:12730
std::map< std::string, surface > reportSurfaces_
Definition: display.hpp:813
std::set< map_location > invalidated_
Definition: display.hpp:818
bool find(E event, F functor)
Tests whether an event handler is available.
cl_event event
Definition: glew.h:3070
void clear_screen()
Clear the screen contents.
Definition: display.cpp:2783
void drawing_buffer_clear()
Clears the drawing buffer.
Definition: display.cpp:1390
surface tile_surface(const surface &surf, int w, int h, bool optimize)
Tile a surface.
Definition: utils.cpp:674
std::map< std::string, config > reports_
Definition: display.hpp:815
const display_context * dc_
Definition: display.hpp:663
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
GLint GLvoid * img
Definition: glew.h:1353
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:135
const color_range & color_info(const std::string &name)
this module manages the cache of images.
Definition: image.cpp:75
double idle_anim_rate_
Definition: display.hpp:1148
void update_tod()
Add r,g,b from tod_manager to the map.
Definition: display.cpp:446
int drawn_hexes_
Definition: display.hpp:1145
Standard logging facilities (interface).
double turbo_speed_
Definition: display.hpp:787
gui::button::TYPE string_to_button_type(std::string type)
Definition: display.cpp:1000
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:248
const std::string & prefix() const
Definition: theme.hpp:143
int diagnostic_label_
Definition: display.hpp:785
void add_redraw_observer(std::function< void(display &)> f)
Adds a redraw observer, a function object to be called when redraw_everything is used.
Definition: display.cpp:2696
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:202
void assign(const surface &o)
Definition: utils.hpp:83
surface create_compatible_surface(const surface &surf, int width, int height)
Definition: utils.cpp:2166
std::string ellipsis
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
void reinit_flags_for_side(size_t side)
Rebuild the flag list (not team colors) for a single side.
Definition: display.cpp:282
static void draw_label(CVideo &video, surface target, const theme::label &label)
Definition: display.cpp:1525
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:360
surface greyscale_image(const surface &surf, bool optimize)
Definition: utils.cpp:761
static std::string get_side_color_index(int side)
Definition: team.cpp:840
bool scroll_to_action()
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1220
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
map_labels & labels()
Definition: display.cpp:2773
#define e
bool outside_area(const SDL_Rect &area, const int x, const int y) const
Check if the bbox of the hex at x,y has pixels outside the area rectangle.
Definition: display.cpp:568
void fill_images_list(const std::string &prefix, std::vector< std::string > &images)
Definition: display.cpp:458
std::vector< animated< image::locator > > flags_
Animated flags for each team.
Definition: display.hpp:855
const SDL_Color YELLOW_COLOR
Definition: font.cpp:570
size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:102
unit_iterator find(size_t id)
Definition: map.cpp:285
std::string team_name
Definition: overlay.hpp:38
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
bool zoom_at_max() const
Definition: display.cpp:2275
boost::scoped_ptr< halo::manager > halo_man_
Definition: display.hpp:664
static void draw_panel(CVideo &video, const theme::panel &panel, std::vector< std::shared_ptr< gui::button >> &)
Definition: display.cpp:1498
size_t font_size() const
Definition: theme.hpp:125
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
surface get_rendered_text(const std::string &str, int size, const SDL_Color &color, int style)
Definition: font.cpp:886
void refresh_report(std::string const &report_name, const config *new_cfg=nullptr)
Redraws the specified report (if anything has changed).
Definition: display.cpp:3099
void remove_single_overlay(const map_location &loc, const std::string &toDelete)
remove_single_overlay will remove a single overlay from a tile
Definition: display.cpp:124
int hex_size() const
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:266
surface get_flag(const map_location &loc)
Definition: display.cpp:358
GLdouble s
Definition: glew.h:1358
std::vector< std::shared_ptr< gui::zoom_slider > > sliders_
Definition: display.hpp:817
const int font_size
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
iterator begin() const
Definition: display.cpp:657
void announce(const std::string &msg, const SDL_Color &color=font::GOOD_COLOR)
Announce a message prominently.
Definition: display.cpp:1950
int add_tooltip(const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup, const surface &foreground)
Definition: tooltips.cpp:180
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:2206
bool turbo_
Definition: display.hpp:788
std::vector< std::string > square_parenthetical_split(std::string const &val, const char separator, std::string const &left, std::string const &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
void add_arrow(arrow &)
Definition: display.cpp:3689
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition: display.cpp:108
void redraw_unit(const unit &u) const
draw a unit.
Definition: drawer.cpp:50
bool animate_map_
Local cache for preferences::animate_map, since it is constantly queried.
Definition: display.hpp:839
#define LOG_DP
Definition: display.cpp:63
static void toggle_debug_foreground()
Toggle to debug foreground terrain.
Definition: display.cpp:1406
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string grid_top
Definition: game_config.cpp:84
Text class.
Definition: text.hpp:66
void draw_rectangle(int x, int y, int w, int h, Uint32 color, surface target)
Draw a colored rectangle on a surface.
Definition: rect.cpp:103
std::string fog_prefix
std::string enemy_color()
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition: display.cpp:836
Definition: display.hpp:47
The border of the map.
Definition: display.hpp:904
config & add_child_at(const std::string &key, const config &val, unsigned index)
Definition: config.cpp:773
int blindfold_ctr_
Definition: display.hpp:659
void draw_floating_labels(surface screen)
TERRAIN_TYPE
Definition: display.hpp:740
bool grid_
Definition: display.hpp:784
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: util.hpp:503
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:418
GLenum target
Definition: glew.h:5190
const int SIZE_SMALL
Definition: font.hpp:62
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:482
bool set_zoom(int amount, bool absolute=false)
Zooms the display by the specified amount.
Definition: display.cpp:2285
bool propagate_invalidation(const std::set< map_location > &locs)
If this set is partially invalidated, invalidate all its hexes.
Definition: display.cpp:3563
const std::string valid
Little parts of regex templates used to parse Wml annoations.
bool screenshot(const std::string &filename, bool map_screenshot=false)
Save a (map-)screenshot and return whether the operation succeeded.
Definition: display.cpp:753
GLclampf f
Definition: glew.h:3024
surface minimap_
Definition: display.hpp:779
void set_team(size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition: display.cpp:379
void start_animation(int start_time, bool cycles=false)
Starts an animation cycle.
ttext & set_foreground_color(const Uint32 color)
Definition: text.cpp:429