The Battle for Wesnoth  1.15.0-dev
minimap.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "gui/widgets/minimap.hpp"
18 
19 #include "gui/core/log.hpp"
23 #include "gui/widgets/settings.hpp"
24 #include "map/map.hpp"
25 #include "map/exception.hpp"
26 #include "sdl/rect.hpp"
27 #include "terrain/type_data.hpp"
28 #include "../../minimap.hpp" // We want the file in src/
29 
30 #include "utils/functional.hpp"
31 
32 #include <algorithm>
33 
34 static lg::log_domain log_config("config");
35 #define ERR_CF LOG_STREAM_INDENT(err, log_config)
36 
37 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
38 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
39 
40 // Define this to enable debug output for the minimap cache.
41 //#define DEBUG_MINIMAP_CACHE
42 
43 namespace gui2
44 {
45 
46 // ------------ WIDGET -----------{
47 
48 REGISTER_WIDGET(minimap)
49 
50 minimap::minimap(const implementation::builder_minimap& builder)
51  : styled_widget(builder, type())
52  , map_data_()
53  , terrain_(nullptr)
54 {
55 }
56 
57 void minimap::set_active(const bool /*active*/)
58 {
59  /* DO NOTHING */
60 }
61 
62 bool minimap::get_active() const
63 {
64  return true;
65 }
66 
67 unsigned minimap::get_state() const
68 {
69  return 0;
70 }
71 
72 /** Key type for the cache. */
73 struct key_type
74 {
75  key_type(const int w, const int h, const std::string& map_data)
76  : w(w), h(h), map_data(map_data)
77  {
78  }
79 
80  /** Width of the image. */
81  const int w;
82 
83  /** Height of the image. */
84  const int h;
85 
86  /** The data used to generate the image. */
87  const std::string map_data;
88 };
89 
90 static bool operator<(const key_type& lhs, const key_type& rhs)
91 {
92  return std::tie(lhs.w, lhs.h, lhs.map_data) < std::tie(rhs.w, rhs.h, rhs.map_data);
93 }
94 
95 /** Value type for the cache. */
96 struct value_type
97 {
98  value_type(const surface& surf) : surf(surf), age(1)
99  {
100  }
101 
102  /** The cached image. */
103  const surface surf;
104 
105  /**
106  * The age of the image.
107  *
108  * Every time an image is used its age is increased by one. Once the cache
109  * is full 25% of the cache is emptied. This is done by halving the age of
110  * the items in the cache and then erase the 25% with the lowest age. If
111  * items have the same age their order is unspecified.
112  */
113  unsigned age;
114 };
115 
116 /**
117  * Maximum number of items in the cache (multiple of 4).
118  *
119  * No testing on the optimal number is done, just seems a nice number.
120  */
121 static const size_t cache_max_size = 100;
122 
123 /**
124  * The terrain used to create the cache.
125  *
126  * If another terrain config is used the cache needs to be cleared, this
127  * normally doesn't happen a lot so the clearing of the cache is rather
128  * unusual.
129  */
130 static const ::config* terrain = nullptr;
131 
132 /** The cache. */
133 typedef std::map<key_type, value_type> tcache;
134 static tcache cache;
135 
136 static bool compare(const std::pair<unsigned, tcache::iterator>& lhs,
137  const std::pair<unsigned, tcache::iterator>& rhs)
138 {
139  return lhs.first < rhs.first;
140 }
141 
142 static void shrink_cache()
143 {
144 #ifdef DEBUG_MINIMAP_CACHE
145  std::cerr << "\nShrink cache from " << cache.size();
146 #else
147  DBG_GUI_D << "Shrinking the minimap cache.\n";
148 #endif
149 
150  std::vector<std::pair<unsigned, tcache::iterator>> items;
151  for(tcache::iterator itor = cache.begin(); itor != cache.end(); ++itor) {
152 
153  itor->second.age /= 2;
154  items.emplace_back(itor->second.age, itor);
155  }
156 
157  std::partial_sort(items.begin(),
158  items.begin() + cache_max_size / 4,
159  items.end(),
160  compare);
161 
162  for(std::vector<std::pair<unsigned, tcache::iterator>>::iterator vitor
163  = items.begin();
164  vitor < items.begin() + cache_max_size / 4;
165  ++vitor) {
166 
167  cache.erase(vitor->second);
168  }
169 
170 #ifdef DEBUG_MINIMAP_CACHE
171  std::cerr << " to " << cache.size() << ".\n";
172 #endif
173 }
174 
176 {
177  return false;
178 }
179 
180 const surface minimap::get_image(const int w, const int h) const
181 {
182  if(!terrain_) {
183  return nullptr;
184  }
185 
186  if(terrain_ != terrain) {
187 #ifdef DEBUG_MINIMAP_CACHE
188  std::cerr << "\nFlush cache.\n";
189 #else
190  DBG_GUI_D << "Flushing the minimap cache.\n";
191 #endif
192  terrain = terrain_;
193  cache.clear();
194  }
195 
196  const key_type key(w, h, map_data_);
197  tcache::iterator itor = cache.find(key);
198 
199  if(itor != cache.end()) {
200 #ifdef DEBUG_MINIMAP_CACHE
201  std::cerr << '+';
202 #endif
203  itor->second.age++;
204  return itor->second.surf;
205  }
206 
207  if(cache.size() >= cache_max_size) {
208  shrink_cache();
209  }
210 
211  try
212  {
213  const gamemap map(std::make_shared<terrain_type_data>(*terrain_), map_data_);
214  const surface surf = image::getMinimap(w, h, map, nullptr, nullptr, true);
215  cache.emplace(key, value_type(surf));
216 #ifdef DEBUG_MINIMAP_CACHE
217  std::cerr << '-';
218 #endif
219  return surf;
220  }
221  catch(const incorrect_map_format_error& e)
222  {
223  ERR_CF << "Error while loading the map: " << e.message << '\n';
224 #ifdef DEBUG_MINIMAP_CACHE
225  std::cerr << 'X';
226 #endif
227  }
228  return nullptr;
229 }
230 
232  int x_offset,
233  int y_offset)
234 {
235  if(!terrain_)
236  return;
237  assert(terrain_);
238 
239  DBG_GUI_D << LOG_HEADER << " size "
240  << calculate_blitting_rectangle(x_offset, y_offset) << ".\n";
241 
242  if(map_data_.empty()) {
243  return;
244  }
245 
246  SDL_Rect rect = calculate_blitting_rectangle(x_offset, y_offset);
247  assert(rect.w > 0 && rect.h > 0);
248 
249  const ::surface surf = get_image(rect.w, rect.h);
250  if(surf) {
251  sdl_blit(surf, nullptr, frame_buffer, &rect);
252  }
253 }
254 
255 // }---------- DEFINITION ---------{
256 
259 {
260  DBG_GUI_P << "Parsing minimap " << id << '\n';
261 
262  load_resolutions<resolution>(cfg);
263 }
264 
265 /*WIKI
266  * @page = GUIWidgetDefinitionWML
267  * @order = 1_minimap
268  *
269  * == Minimap ==
270  *
271  * @macro = minimap_description
272  *
273  * The following states exist:
274  * * state_enabled, the minimap is enabled.
275  * @begin{parent}{name="gui/"}
276  * @begin{tag}{name="minimap_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
277  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
278  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
279  * @end{tag}{name="state_enabled"}
280  * @end{tag}{name="resolution"}
281  * @end{tag}{name="minimap_definition"}
282  * @end{parent}{name="gui/"}
283  */
285  : resolution_definition(cfg)
286 {
287  // Note the order should be the same as the enum state_t in minimap.hpp.
288  state.emplace_back(cfg.child("state_enabled"));
289 }
290 
291 // }---------- BUILDER -----------{
292 
293 /*WIKI_MACRO
294  * @begin{macro}{minimap_description}
295  *
296  * A minimap to show the gamemap, this only shows the map and has no
297  * interaction options. This version is used for map previews, there
298  * will be a another version which allows interaction.
299  * @end{macro}
300  */
301 
302 /*WIKI
303  * @page = GUIWidgetInstanceWML
304  * @order = 2_minimap
305  *
306  * == Minimap ==
307  *
308  * @macro = minimap_description
309  *
310  * A minimap has no extra fields.
311  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
312  * @begin{tag}{name="minimap"}{min=0}{max=-1}{super="generic/widget_instance"}
313  * @end{tag}{name="minimap"}
314  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
315  */
316 
317 namespace implementation
318 {
319 
320 builder_minimap::builder_minimap(const config& cfg) : builder_styled_widget(cfg)
321 {
322 }
323 
325 {
326  minimap* widget = new minimap(*this);
327 
328  DBG_GUI_G << "Window builder: placed minimap '" << id
329  << "' with definition '" << definition << "'.\n";
330 
331  return widget;
332 }
333 
334 } // namespace implementation
335 
336 // }------------ END --------------
337 
338 } // namespace gui2
Define the common log macros for the gui toolkit.
Base class of a resolution, contains the common keys for a resolution.
static bool compare(const std::pair< unsigned, tcache::iterator > &lhs, const std::pair< unsigned, tcache::iterator > &rhs)
Definition: minimap.cpp:136
#define DBG_GUI_P
Definition: log.hpp:68
config & child(config_key_type 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:423
std::vector< state_definition > state
const surface get_image(const int w, const int h) const
Gets the image for the minimap.
Definition: minimap.cpp:180
SDL_Rect calculate_blitting_rectangle(const int x_offset, const int y_offset)
Calculates the blitting rectangle of the widget.
Definition: widget.cpp:349
std::string map_data_
The map data to be used to generate the map.
Definition: minimap.hpp:85
Base class for all widgets.
Definition: widget.hpp:47
const std::string & map_data() const
Definition: minimap.hpp:73
#define h
const std::vector< std::string > items
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map, bool ignore_terrain_disabled)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:39
Generic file dialog.
Definition: field-fwd.hpp:22
value_type(const surface &surf)
Definition: minimap.cpp:98
static void shrink_cache()
Definition: minimap.cpp:142
#define DBG_GUI_D
Definition: log.hpp:28
virtual bool get_active() const override
See styled_widget::get_active.
Definition: minimap.cpp:62
const int h
Height of the image.
Definition: minimap.cpp:84
static lg::log_domain log_config("config")
#define ERR_CF
Definition: minimap.cpp:35
std::string definition
Parameters for the styled_widget.
unsigned age
The age of the image.
Definition: minimap.cpp:113
This file contains the settings handling of the widget library.
std::map< key_type, value_type > tcache
The cache.
Definition: minimap.cpp:133
Encapsulates the map of the game.
Definition: map.hpp:36
static bool operator<(const key_type &lhs, const key_type &rhs)
Definition: minimap.cpp:90
Value type for the cache.
Definition: minimap.cpp:96
const ::config * terrain_
The config object with the terrain data.
Definition: minimap.hpp:92
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
Definition: minimap.cpp:175
const std::string map_data
The data used to generate the image.
Definition: minimap.cpp:87
virtual void impl_draw_background(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_background.
Definition: minimap.cpp:231
#define LOG_HEADER
Definition: minimap.cpp:38
static tcache cache
Definition: minimap.cpp:134
minimap_definition(const config &cfg)
Definition: minimap.cpp:257
int w
Base class for all visible items.
The basic minimap class.
Definition: minimap.hpp:39
static const size_t cache_max_size
Maximum number of items in the cache (multiple of 4).
Definition: minimap.cpp:121
const int w
Width of the image.
Definition: minimap.cpp:81
Contains the SDL_Rect helper code.
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: minimap.cpp:57
key_type(const int w, const int h, const std::string &map_data)
Definition: minimap.cpp:75
std::string message
Definition: exceptions.hpp:31
#define e
virtual unsigned get_state() const override
See styled_widget::get_state.
Definition: minimap.cpp:67
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
#define DBG_GUI_G
Definition: log.hpp:40
Key type for the cache.
Definition: minimap.cpp:73
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
const surface surf
The cached image.
Definition: minimap.cpp:103